mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-09-30 20:21:14 +00:00
Compare commits
66 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e52e920b14 | |||
| d58ab98794 | |||
| b84e63f06b | |||
| 2db4c35c54 | |||
| d3d26797c1 | |||
| 99a65383f1 | |||
| 5f47a7f064 | |||
| 9c0d41b55f | |||
| 2cc7a94053 | |||
| b30e68a599 | |||
| 4e1f277432 | |||
| 9b0008008a | |||
| e47aee1215 | |||
| 61fd747cbd | |||
| bfcdcdc145 | |||
| 7a80ce616b | |||
| ed8d2d9ded | |||
| 4e5f0cd4e1 | |||
| f8685e0c92 | |||
| 2b952b79d7 | |||
| c040f73794 | |||
| 08c284fcb8 | |||
| 16b103aa6c | |||
| 36bd1c262a | |||
| 39ee32a311 | |||
| 51224713b7 | |||
| 08e5861af9 | |||
| a3ecd2b6cb | |||
| 70b4070709 | |||
| 72bc40c50b | |||
| 76b1180d02 | |||
| 23e11d2a58 | |||
| ff4ae20a88 | |||
| 37d4c6e636 | |||
| a06a5377ad | |||
| 684188d375 | |||
| 6e69740ab7 | |||
| f6d6e3c195 | |||
| 695a40872a | |||
| 897f9d5029 | |||
| e79ece6518 | |||
| 3d6974607e | |||
| d85d576b0d | |||
| e4c55e1aff | |||
| 8c3404960a | |||
| 0325b44d8e | |||
| a86e24731d | |||
| aafbcd6ce1 | |||
| 2c57681ca1 | |||
| 5f142c7926 | |||
| c5f6f1ca5e | |||
| dcbcdf16ad | |||
| ed57836903 | |||
| 20f9aa4cfe | |||
| 6f6a8f6d67 | |||
| 2f787ddb8c | |||
| 23585820bc | |||
| e5b8b10188 | |||
| 8a10109e24 | |||
| a538cd9aff | |||
| ce202292cf | |||
| 87e5d0b6e4 | |||
| fdd8d4252c | |||
| 1036982d2f | |||
| e27650cf42 | |||
| ecb2ae4076 |
@@ -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
|
||||
|
||||
@@ -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
@@ -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
|
||||
```
|
||||
|
||||
@@ -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.
|
||||
@@ -7,11 +7,12 @@ A dynamic recompiler for ARM.
|
||||
|
||||
Cortex-A57 (32 and 64 bit) is the emulated guest target. See [ArchVersion](../../src/dynarmic/src/dynarmic/interface/A32/arch_version.h).
|
||||
|
||||
The only supported host architectures are x86-64, and AArch64. There are no plans to support any 32-bit architectures.
|
||||
The only supported host architectures are x86-64, PowerPC 64, and AArch64. There are no plans to support any 32-bit architectures.
|
||||
|
||||
See [an example usage](../../src/dynarmic/tests/print_info.cpp).
|
||||
|
||||
Design documentation can be found at [./Design.md](./Design.md).
|
||||
PowerPC design documentation can be found at [./PowerPC](./PowerPC.md).
|
||||
|
||||
Alternatives to Dynarmic
|
||||
------------------------
|
||||
|
||||
Vendored
+5
@@ -249,6 +249,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
@@ -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"
|
||||
|
||||
Vendored
+10
@@ -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)
|
||||
Vendored
+145
@@ -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
|
||||
+6
@@ -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
|
||||
Vendored
+222
@@ -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,
|
||||
Vendored
+343
@@ -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;
|
||||
}
|
||||
Vendored
+401
@@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
Vendored
+487
@@ -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); }
|
||||
Vendored
+340
@@ -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
@@ -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()
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
<!-- General application strings -->
|
||||
<string name="app_name" translatable="false">Eden</string>
|
||||
<string name="app_disclaimer">This software will run games for the Nintendo Switch game console. No game titles or keys are included.<br /><br />Before you begin, please locate your <![CDATA[<b> prod.keys </b>]]> file on your device storage.<br /><br /><![CDATA[<a href=\"https://yuzu-mirror.github.io/help/quickstart\">Learn more</a>]]></string>
|
||||
<string name="app_disclaimer">This software will run games for the Nintendo Switch game console. No game titles or keys are included.<br /><br />Before you begin, please locate your <![CDATA[<b> prod.keys </b>]]> file on your device storage.<br /><br /><![CDATA[<a href=\"https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/QuickStart.md\">Learn more</a>]]></string>
|
||||
<string name="notice_notification_channel_name">Notices and errors</string>
|
||||
<string name="notice_notification_channel_id" translatable="false">noticesAndErrors</string>
|
||||
<string name="notice_notification_channel_description">Shows notifications when something goes wrong.</string>
|
||||
@@ -933,7 +933,7 @@
|
||||
<string name="loader_error_file_not_found">ROM file does not exist</string>
|
||||
|
||||
<string name="loader_requires_firmware">Game Requires Firmware</string>
|
||||
<string name="loader_requires_firmware_description"><![CDATA[The game you are trying to launch requires firmware to boot or to get past the opening menu. Please <a href=\"https://yuzu-mirror.github.io/help/quickstart\"> dump and install firmware</a>, or press \"OK\" to launch anyways.]]></string>
|
||||
<string name="loader_requires_firmware_description"><![CDATA[This game may require firmware to function properly, and you don\'t have any installed. Please install firmware before launching, or press \"OK\" to launch anyways.]]></string>
|
||||
|
||||
<!-- Intent Launch strings -->
|
||||
<string name="searching_for_game">Searching for game...</string>
|
||||
|
||||
@@ -89,7 +89,6 @@ add_library(
|
||||
param_package.h
|
||||
parent_of_member.h
|
||||
point.h
|
||||
quaternion.h
|
||||
range_map.h
|
||||
range_mutex.h
|
||||
range_sets.h
|
||||
@@ -109,6 +108,8 @@ add_library(
|
||||
settings_setting.h
|
||||
slot_vector.h
|
||||
socket_types.h
|
||||
sparse_large_vector.cpp
|
||||
sparse_large_vector.h
|
||||
spin_lock.h
|
||||
stb.cpp
|
||||
stb.h
|
||||
@@ -138,8 +139,6 @@ add_library(
|
||||
uuid.cpp
|
||||
uuid.h
|
||||
vector_math.h
|
||||
virtual_buffer.cpp
|
||||
virtual_buffer.h
|
||||
zstd_compression.cpp
|
||||
zstd_compression.h
|
||||
fs/ryujinx_compat.h fs/ryujinx_compat.cpp
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/fiber.h"
|
||||
#include "common/virtual_buffer.h"
|
||||
|
||||
#include <boost/context/detail/fcontext.hpp>
|
||||
|
||||
|
||||
+40
-13
@@ -117,7 +117,6 @@ template <typename T>
|
||||
static void GetFuncAddress(Common::DynamicLibrary& dll, const char* name, T& pfn) {
|
||||
if (!dll.GetSymbol(name, &pfn)) {
|
||||
LOG_CRITICAL(HW_Memory, "Failed to load {}", name);
|
||||
throw std::bad_alloc{};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,6 +178,14 @@ public:
|
||||
Release();
|
||||
}
|
||||
|
||||
void* Allocate(size_t size) {
|
||||
auto* ptr = VirtualAlloc(nullptr, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
|
||||
if (ptr == nullptr) {
|
||||
LOG_CRITICAL(HW_Memory, "Failed to allocate fallback buffer with size {:#x}, error {}", size, GetLastError());
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perms) {
|
||||
std::unique_lock lock{placeholder_mutex};
|
||||
if (!IsNiechePlaceholder(virtual_offset, length)) {
|
||||
@@ -399,6 +406,10 @@ private:
|
||||
// For managarm: see https://github.com/managarm/managarm/issues/1370
|
||||
#else // ^^^ Windows ^^^ vvv POSIX vvv
|
||||
|
||||
#ifndef MAP_NOCORE
|
||||
#define MAP_NOCORE 0
|
||||
#endif
|
||||
|
||||
#ifdef ARCHITECTURE_arm64
|
||||
|
||||
static void* ChooseVirtualBase(size_t virtual_size) {
|
||||
@@ -423,7 +434,7 @@ static void* ChooseVirtualBase(size_t virtual_size) {
|
||||
// Note: we may be able to take advantage of MAP_FIXED_NOREPLACE here.
|
||||
void* map_pointer =
|
||||
mmap(reinterpret_cast<void*>(hint_address), virtual_size, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0);
|
||||
MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE | MAP_NOCORE, -1, 0);
|
||||
|
||||
// If we successfully mapped, we're done.
|
||||
if (reinterpret_cast<uintptr_t>(map_pointer) == hint_address) {
|
||||
@@ -443,11 +454,11 @@ static void* ChooseVirtualBase(size_t virtual_size) {
|
||||
|
||||
static void* ChooseVirtualBase(size_t virtual_size) {
|
||||
#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__OpenBSD__) || defined(__sun__) || defined(__HAIKU__) || defined(__managarm__) || defined(__AIX__)
|
||||
void* virtual_base = mmap(nullptr, virtual_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE | MAP_ALIGNED_SUPER, -1, 0);
|
||||
void* virtual_base = mmap(nullptr, virtual_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE | MAP_ALIGNED_SUPER | MAP_NOCORE, -1, 0);
|
||||
if (virtual_base != MAP_FAILED)
|
||||
return virtual_base;
|
||||
#endif
|
||||
return mmap(nullptr, virtual_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0);
|
||||
return mmap(nullptr, virtual_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE | MAP_NOCORE, -1, 0);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -541,13 +552,13 @@ public:
|
||||
}
|
||||
if (use_anon) {
|
||||
LOG_WARNING(Common_Memory, "Using private mappings instead of shared ones");
|
||||
backing_base = static_cast<u8*>(mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0));
|
||||
backing_base = static_cast<u8*>(mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_NOCORE, -1, 0));
|
||||
if (fd > 0) {
|
||||
fd = -1;
|
||||
close(fd);
|
||||
}
|
||||
} else {
|
||||
backing_base = static_cast<u8*>(mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0));
|
||||
backing_base = static_cast<u8*>(mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_NOCORE, fd, 0));
|
||||
}
|
||||
if (backing_base == MAP_FAILED) {
|
||||
LOG_CRITICAL(HW_Memory, "mmap failed: {}", strerror(errno));
|
||||
@@ -571,6 +582,14 @@ public:
|
||||
Release();
|
||||
}
|
||||
|
||||
void* Allocate(size_t size) {
|
||||
auto* ptr = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
|
||||
if (ptr == MAP_FAILED) {
|
||||
LOG_CRITICAL(HW_Memory, "Failed to allocate fallback buffer with size {:#x}, {}", size, strerror(errno));
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perms) {
|
||||
// Intersect the range with our address space.
|
||||
AdjustMap(&virtual_offset, &length);
|
||||
@@ -691,12 +710,10 @@ HostMemory::HostMemory(size_t backing_size_, size_t virtual_size_)
|
||||
{
|
||||
#if defined(__OPENORBIS__) || defined(__managarm__)
|
||||
LOG_WARNING(HW_Memory, "Platform doesn't support fastmem");
|
||||
fallback_buffer.emplace(backing_size);
|
||||
backing_base = fallback_buffer->data();
|
||||
backing_base = static_cast<u8*>(mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
|
||||
virtual_base = nullptr;
|
||||
#else
|
||||
// Try to allocate a fastmem arena.
|
||||
// The implementation will fail with std::bad_alloc on errors.
|
||||
impl = std::make_unique<HostMemory::Impl>(AlignUp(backing_size, PageAlignment), AlignUp(virtual_size, PageAlignment) + HugePageSize);
|
||||
if (impl->Init()) {
|
||||
backing_base = impl->backing_base;
|
||||
@@ -707,16 +724,26 @@ HostMemory::HostMemory(size_t backing_size_, size_t virtual_size_)
|
||||
virtual_base_offset = virtual_base - impl->virtual_base;
|
||||
}
|
||||
} else {
|
||||
impl.reset();
|
||||
LOG_WARNING(HW_Memory, "Platform can support fastmem, but can't create it");
|
||||
fallback_buffer.emplace(backing_size);
|
||||
backing_base = fallback_buffer->data();
|
||||
fallback_buffer = true;
|
||||
backing_base = static_cast<u8*>(impl->Allocate(backing_size));
|
||||
virtual_base = nullptr;
|
||||
impl.reset();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
HostMemory::~HostMemory() = default;
|
||||
HostMemory::~HostMemory() {
|
||||
#ifdef _WIN32
|
||||
if (fallback_buffer) {
|
||||
VirtualFree(backing_base, backing_size, MEM_RELEASE);
|
||||
}
|
||||
#else
|
||||
if (fallback_buffer) {
|
||||
munmap(backing_base, backing_size);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
HostMemory::HostMemory(HostMemory&&) noexcept = default;
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#include <optional>
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/virtual_buffer.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
@@ -86,7 +85,7 @@ private:
|
||||
u8* virtual_base{};
|
||||
size_t virtual_base_offset{};
|
||||
// Windows requires it for kernels whom lack proper support for some functions!
|
||||
std::optional<Common::VirtualBuffer<u8>> fallback_buffer;
|
||||
bool fallback_buffer{false};
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
||||
@@ -13,39 +13,11 @@ PageTable::PageTable() = default;
|
||||
|
||||
PageTable::~PageTable() noexcept = default;
|
||||
|
||||
bool PageTable::BeginTraversal(TraversalEntry* out_entry, TraversalContext* out_context,
|
||||
Common::ProcessAddress address) const {
|
||||
out_context->next_offset = GetInteger(address);
|
||||
out_context->next_page = address / page_size;
|
||||
|
||||
return this->ContinueTraversal(out_entry, out_context);
|
||||
}
|
||||
|
||||
bool PageTable::ContinueTraversal(TraversalEntry* out_entry, TraversalContext* context) const {
|
||||
// Setup invalid defaults.
|
||||
out_entry->phys_addr = 0;
|
||||
out_entry->block_size = page_size;
|
||||
// Validate that we can read the actual entry.
|
||||
if (auto const page = context->next_page; page < entries.size()) {
|
||||
// Validate that the entry is mapped.
|
||||
if (auto const paddr = entries[page].addr; paddr != 0) {
|
||||
// Populate the results.
|
||||
out_entry->phys_addr = paddr + context->next_offset;
|
||||
context->next_page += 1;
|
||||
context->next_offset += page_size;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
context->next_page += 1;
|
||||
context->next_offset += page_size;
|
||||
return false;
|
||||
}
|
||||
|
||||
void PageTable::Resize(std::size_t address_space_width_in_bits, std::size_t page_size_in_bits) {
|
||||
auto const num_page_table_entries = 1ULL << (address_space_width_in_bits - page_size_in_bits);
|
||||
entries.resize(num_page_table_entries);
|
||||
void PageTable::Resize(std::size_t address_space_width_in_bits, std::size_t page_bits) {
|
||||
auto const num_page_table_entries = 1ULL << (address_space_width_in_bits - page_bits);
|
||||
entries.ResizeAndClear(num_page_table_entries);
|
||||
current_address_space_width_in_bits = address_space_width_in_bits;
|
||||
page_size = 1ULL << page_size_in_bits;
|
||||
current_page_bits = page_bits;
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
|
||||
+64
-56
@@ -9,22 +9,22 @@
|
||||
#include <atomic>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/sparse_large_vector.h"
|
||||
#include "common/typed_address.h"
|
||||
#include "common/virtual_buffer.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
enum class PageType : u8 {
|
||||
/// Page is unmapped and should cause an access error.
|
||||
Unmapped,
|
||||
Unmapped = 0b00,
|
||||
/// Page is mapped to regular memory. This is the only type you can get pointers to.
|
||||
Memory,
|
||||
Memory = 0b01,
|
||||
/// Page is mapped to regular memory, but inaccessible from CPU fastmem and must use
|
||||
/// the callbacks.
|
||||
DebugMemory,
|
||||
DebugMemory = 0b10,
|
||||
/// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and
|
||||
/// invalidation
|
||||
RasterizerCachedMemory,
|
||||
RasterizerCachedMemory = 0b11,
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -42,57 +42,86 @@ struct PageTable {
|
||||
u64 next_offset{};
|
||||
};
|
||||
|
||||
/// Number of bits reserved for attribute tagging.
|
||||
/// This can be at most the guaranteed alignment of the pointers in the page table.
|
||||
static constexpr int ATTRIBUTE_BITS = 2;
|
||||
/// Masks out bits reserved for attribute tagging.
|
||||
static constexpr u64 ATTRIBUTE_MASK = ((1ULL << 44) - 1) << 12;
|
||||
|
||||
/// Specifies sign bit for page table entries.
|
||||
static constexpr u64 SIGN_BIT = 45 + 12; // 44 bits of data + page offset
|
||||
|
||||
/**
|
||||
* Pair of host pointer and page type attribute.
|
||||
* This uses the lower bits of a given pointer to store the attribute tag.
|
||||
* Atomic tuple of host pointer, page type, and block id.
|
||||
* This uses the lower bits of a given pointer to store the attributes.
|
||||
* Writing and reading the pointer attribute pair is guaranteed to be atomic for the same method
|
||||
* call. In other words, they are guaranteed to be synchronized at all times.
|
||||
*/
|
||||
class PageInfo {
|
||||
class PageEntryData {
|
||||
public:
|
||||
struct Data {
|
||||
Data(bool marked_, PageType type_, u16 block_, u64 page_)
|
||||
: marked(static_cast<u64>(marked_) & 0b1)
|
||||
, type(static_cast<u64>(type_) & ((1ULL << 2) - 1))
|
||||
, block(static_cast<u64>(block_) & ((1ULL << 9) - 1))
|
||||
, page((page_ >> 12) & ((1ULL << 45) - 1))
|
||||
, block2((static_cast<u64>(block_) >> 9) & ((1ULL << 7) - 1)) {}
|
||||
u64 marked : 1;
|
||||
u64 type : 2;
|
||||
u64 block : 9;
|
||||
u64 page : 45; // 44 bits of actual data (64 - page offset (12) - reserved (8)) + a sign bit
|
||||
u64 block2 : 7;
|
||||
};
|
||||
|
||||
[[nodiscard]] Data Raw() const noexcept {
|
||||
return std::bit_cast<Data>(data_raw.load(std::memory_order_relaxed));
|
||||
}
|
||||
|
||||
/// Returns the page pointer
|
||||
[[nodiscard]] uintptr_t Pointer() const noexcept {
|
||||
return ExtractPointer(raw.load(std::memory_order_relaxed));
|
||||
[[nodiscard]] uintptr_t Pointer(bool ignored_marked = false) const noexcept {
|
||||
return ExtractPointer(std::bit_cast<Data>(data_raw.load(std::memory_order_relaxed)), ignored_marked);
|
||||
}
|
||||
|
||||
/// Returns the page type attribute
|
||||
[[nodiscard]] PageType Type() const noexcept {
|
||||
return ExtractType(raw.load(std::memory_order_relaxed));
|
||||
return static_cast<PageType>(std::bit_cast<Data>(data_raw.load(std::memory_order_relaxed)).type);
|
||||
}
|
||||
|
||||
/// Returns the block identifier.
|
||||
[[nodiscard]] u16 Block() const noexcept {
|
||||
return ExtractBlock(std::bit_cast<Data>(data_raw.load(std::memory_order_relaxed)));
|
||||
}
|
||||
|
||||
/// Returns the page pointer and attribute pair, extracted from the same atomic read
|
||||
[[nodiscard]] std::pair<uintptr_t, PageType> PointerType() const noexcept {
|
||||
const uintptr_t non_atomic_raw = raw.load(std::memory_order_relaxed);
|
||||
return {ExtractPointer(non_atomic_raw), ExtractType(non_atomic_raw)};
|
||||
[[nodiscard]] std::tuple<uintptr_t, PageType, u16> PointerTypeBlock(bool ignore_marked = false) const noexcept {
|
||||
const auto non_atomic_raw = std::bit_cast<Data>(data_raw.load(std::memory_order_relaxed));
|
||||
return {ExtractPointer(non_atomic_raw, ignore_marked), static_cast<PageType>(non_atomic_raw.type), ExtractBlock(non_atomic_raw)};
|
||||
}
|
||||
|
||||
/// Returns the raw representation of the page information.
|
||||
/// Use ExtractPointer and ExtractType to unpack the value.
|
||||
[[nodiscard]] uintptr_t Raw() const noexcept {
|
||||
return raw.load(std::memory_order_relaxed);
|
||||
/// Write page info atomically
|
||||
constexpr void Store(bool marked, PageType type, u16 block, uintptr_t pointer) noexcept {
|
||||
data_raw.store(std::bit_cast<u64>(Data{marked, type, block, pointer}));
|
||||
}
|
||||
|
||||
/// Write a page pointer and type pair atomically
|
||||
void Store(uintptr_t pointer, PageType type) noexcept {
|
||||
raw.store(pointer | uintptr_t(type));
|
||||
constexpr void MarkRasterizerCached() noexcept {
|
||||
data_raw.fetch_or(0b111);
|
||||
}
|
||||
|
||||
constexpr void MarkDebug(u64 ptr, u16 block) noexcept {
|
||||
Store(true, PageType::DebugMemory, block, ptr);
|
||||
}
|
||||
|
||||
/// Unpack a pointer from a page info raw representation
|
||||
[[nodiscard]] static uintptr_t ExtractPointer(uintptr_t raw) noexcept {
|
||||
return raw & (~uintptr_t{0} << ATTRIBUTE_BITS);
|
||||
[[nodiscard]] static uintptr_t ExtractPointer(Data raw, bool ignore_marked = false) noexcept {
|
||||
return raw.marked && !ignore_marked ? 0
|
||||
// shift raw.page's fake sign bit to the actual sign bit, then sign extend
|
||||
: ((s64)(raw.page << (64 - 44))) >> (64 - 44 - 12);
|
||||
}
|
||||
|
||||
/// Unpack a page type from a page info raw representation
|
||||
[[nodiscard]] static PageType ExtractType(uintptr_t raw) noexcept {
|
||||
return static_cast<PageType>(raw & ((uintptr_t{1} << ATTRIBUTE_BITS) - 1));
|
||||
[[nodiscard]] static u16 ExtractBlock(Data raw) noexcept {
|
||||
return static_cast<u16>(raw.block | (raw.block2 << 9));
|
||||
}
|
||||
|
||||
private:
|
||||
std::atomic<uintptr_t> raw;
|
||||
std::atomic<u64> data_raw;
|
||||
static_assert(sizeof(Data) == sizeof(std::atomic<u64>));
|
||||
};
|
||||
|
||||
PageTable();
|
||||
@@ -100,13 +129,8 @@ struct PageTable {
|
||||
|
||||
PageTable(const PageTable&) = delete;
|
||||
PageTable& operator=(const PageTable&) = delete;
|
||||
|
||||
PageTable(PageTable&&) noexcept = default;
|
||||
PageTable& operator=(PageTable&&) noexcept = default;
|
||||
|
||||
bool BeginTraversal(TraversalEntry* out_entry, TraversalContext* out_context,
|
||||
Common::ProcessAddress address) const;
|
||||
bool ContinueTraversal(TraversalEntry* out_entry, TraversalContext* context) const;
|
||||
PageTable(PageTable&&) noexcept = delete;
|
||||
PageTable& operator=(PageTable&&) noexcept = delete;
|
||||
|
||||
/**
|
||||
* Resizes the page table to be able to accommodate enough pages within
|
||||
@@ -121,30 +145,14 @@ struct PageTable {
|
||||
return current_address_space_width_in_bits;
|
||||
}
|
||||
|
||||
bool GetPhysicalAddress(Common::PhysicalAddress* out_phys_addr,
|
||||
Common::ProcessAddress virt_addr) const {
|
||||
if (virt_addr > (1ULL << this->GetAddressSpaceBits())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*out_phys_addr = entries[virt_addr / page_size].addr + GetInteger(virt_addr);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Vector of memory pointers backing each page. An entry can only be non-null if the
|
||||
/// corresponding attribute element is of type `Memory`.
|
||||
struct PageEntryData {
|
||||
PageInfo ptr;
|
||||
u64 block;
|
||||
u64 addr;
|
||||
u64 padding;
|
||||
};
|
||||
VirtualBuffer<PageEntryData> entries;
|
||||
static_assert(sizeof(PageEntryData) == 32);
|
||||
SparseLargeVector<PageEntryData> entries;
|
||||
static_assert(sizeof(PageEntryData) == 8);
|
||||
|
||||
u8* fastmem_arena{};
|
||||
std::size_t current_address_space_width_in_bits{};
|
||||
std::size_t page_size{};
|
||||
std::size_t current_page_bits{};
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2016 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/vector_math.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
template <typename T>
|
||||
class Quaternion {
|
||||
public:
|
||||
Vec3<T> xyz;
|
||||
T w{};
|
||||
|
||||
[[nodiscard]] Quaternion<decltype(-T{})> Inverse() const {
|
||||
return {-xyz, w};
|
||||
}
|
||||
|
||||
[[nodiscard]] Quaternion<decltype(T{} + T{})> operator+(const Quaternion& other) const {
|
||||
return {xyz + other.xyz, w + other.w};
|
||||
}
|
||||
|
||||
[[nodiscard]] Quaternion<decltype(T{} - T{})> operator-(const Quaternion& other) const {
|
||||
return {xyz - other.xyz, w - other.w};
|
||||
}
|
||||
|
||||
[[nodiscard]] Quaternion<decltype(T{} * T{} - T{} * T{})> operator*(
|
||||
const Quaternion& other) const {
|
||||
return {xyz * other.w + other.xyz * w + Cross(xyz, other.xyz),
|
||||
w * other.w - Dot(xyz, other.xyz)};
|
||||
}
|
||||
|
||||
[[nodiscard]] Quaternion<T> Normalized() const {
|
||||
T length = std::sqrt(xyz.Length2() + w * w);
|
||||
return {xyz / length, w / length};
|
||||
}
|
||||
|
||||
[[nodiscard]] std::array<decltype(-T{}), 16> ToMatrix() const {
|
||||
const T x2 = xyz[0] * xyz[0];
|
||||
const T y2 = xyz[1] * xyz[1];
|
||||
const T z2 = xyz[2] * xyz[2];
|
||||
|
||||
const T xy = xyz[0] * xyz[1];
|
||||
const T wz = w * xyz[2];
|
||||
const T xz = xyz[0] * xyz[2];
|
||||
const T wy = w * xyz[1];
|
||||
const T yz = xyz[1] * xyz[2];
|
||||
const T wx = w * xyz[0];
|
||||
|
||||
return {1.0f - 2.0f * (y2 + z2),
|
||||
2.0f * (xy + wz),
|
||||
2.0f * (xz - wy),
|
||||
0.0f,
|
||||
2.0f * (xy - wz),
|
||||
1.0f - 2.0f * (x2 + z2),
|
||||
2.0f * (yz + wx),
|
||||
0.0f,
|
||||
2.0f * (xz + wy),
|
||||
2.0f * (yz - wx),
|
||||
1.0f - 2.0f * (x2 + y2),
|
||||
0.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
1.0f};
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] auto QuaternionRotate(const Quaternion<T>& q, const Vec3<T>& v) {
|
||||
return v + 2 * Cross(q.xyz, Cross(q.xyz, v) + v * q.w);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline Quaternion<float> MakeQuaternion(const Vec3<float>& axis, float angle) {
|
||||
return {axis * std::sin(angle / 2), std::cos(angle / 2)};
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
@@ -858,7 +858,7 @@ struct Values {
|
||||
SwitchableSetting<std::string> program_args{linkage,
|
||||
std::string(),
|
||||
"program_args",
|
||||
Category::System,
|
||||
Category::Debugging,
|
||||
Specialization::Default,
|
||||
true, // save_ - persist in config file
|
||||
false}; // runtime_modifiable_ - startup-only
|
||||
@@ -904,7 +904,7 @@ struct Values {
|
||||
0,
|
||||
65535,
|
||||
"debug_knobs",
|
||||
Category::System,
|
||||
Category::Debugging,
|
||||
Specialization::Countable,
|
||||
true,
|
||||
true};
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/* virtual_buffer.cpp */
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#include <mutex>
|
||||
#else
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/sparse_large_vector.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
#ifdef _WIN32
|
||||
static std::vector<std::pair<u64, u64>> vector_regions {};
|
||||
|
||||
// Workaround for handling non-commited memory accessed by Dynarmic; usually result of an error
|
||||
static LONG WINAPI FakePageFaultHandler(PEXCEPTION_POINTERS info) {
|
||||
DWORD code = info->ExceptionRecord->ExceptionCode;
|
||||
u64 exception_addr = reinterpret_cast<u64>(info->ExceptionRecord->ExceptionAddress);
|
||||
|
||||
if (code != EXCEPTION_ACCESS_VIOLATION) {
|
||||
// Not our problem
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
u64 addr = 0, addr2 = 0;
|
||||
|
||||
for (auto region: vector_regions) {
|
||||
auto addr_shifted = exception_addr >> HostPageBits;
|
||||
if (region.first <= addr_shifted && addr_shifted <= region.second) {
|
||||
addr = addr_shifted;
|
||||
}
|
||||
|
||||
// Page-boundary accesses
|
||||
if (auto addr_ = (exception_addr + 0x40) >> HostPageBits; addr_ != addr_shifted && region.first <= addr_ && addr_ <= region.second) {
|
||||
addr2 = addr_;
|
||||
}
|
||||
|
||||
if (addr != 0 || addr2 != 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (addr == 0 && addr2 == 0) {
|
||||
// Not our problem
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
LOG_ERROR(HW_Memory, "Accessing an unallocated region of a SparseLargeVector at {:#x}; this shouldn't happen and is likely a Dynarmic error!", exception_addr);
|
||||
|
||||
// Commit this region
|
||||
if (addr != 0) {
|
||||
if (!CommitVectorPage(addr << HostPageBits, false)) {
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
}
|
||||
// Commit next region if needed
|
||||
if (addr2 != 0) {
|
||||
if (!CommitVectorPage(addr2 << HostPageBits, false)) {
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
}
|
||||
|
||||
return EXCEPTION_CONTINUE_EXECUTION;
|
||||
}
|
||||
|
||||
bool CommitVectorPage(uintptr_t addr, bool write) noexcept {
|
||||
MEMORY_BASIC_INFORMATION info {};
|
||||
auto res = VirtualQuery(reinterpret_cast<void*>(addr), &info, sizeof(info));
|
||||
if (res == 0) {
|
||||
LOG_CRITICAL(HW_Memory, "Failed to query large buffer region at {:#x} with error {}, will try committing anyway", addr, GetLastError());
|
||||
} else if (info.State != MEM_RESERVE) {
|
||||
LOG_ERROR(HW_Memory, "Tried to commit an unreserved large buffer region at {:#x} that is not mapped or is already committed (state {:#x})", addr, info.State);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto perm = write ? PAGE_READWRITE : PAGE_READONLY;
|
||||
void* res2 = VirtualAlloc(reinterpret_cast<LPVOID>(addr), HostPageSize, MEM_COMMIT, perm);
|
||||
if (res2 == nullptr) {
|
||||
LOG_ERROR(HW_Memory, "Failed to commit large buffer region at {:#x}, error {}", addr, GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef MAP_NOCORE
|
||||
#define MAP_NOCORE 0
|
||||
#endif
|
||||
|
||||
void* AllocateMemoryPages(std::size_t size) noexcept {
|
||||
if (auto page = HostPageSize; size % page != 0) {
|
||||
LOG_WARNING(HW_Memory, "Allocating unaligned large vector with size {:#x}; aligning to {} page size", size, page);
|
||||
size = AlignUp(size, page);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
// We will never use this memory entirely so instead of committing it up front let's just reserve it and commit each page individually
|
||||
void* base = VirtualAlloc(nullptr, size, MEM_RESERVE, PAGE_READWRITE);
|
||||
|
||||
if (base != nullptr) {
|
||||
vector_regions.emplace_back(reinterpret_cast<u64>(base), reinterpret_cast<u64>(base) + size);
|
||||
|
||||
static std::once_flag flag;
|
||||
std::call_once(flag, []() { AddVectoredExceptionHandler(1, FakePageFaultHandler); });
|
||||
} else {
|
||||
// Try committing everything instead??
|
||||
LOG_WARNING(HW_Memory, "Failed to reserve large vector region with error {}, trying to commit instead..", GetLastError());
|
||||
base = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_READWRITE);
|
||||
}
|
||||
ASSERT_MSG(base, "Failed to reserve {:#x} sized region with error {}", size, GetLastError());
|
||||
#else
|
||||
void* base = mmap(nullptr, size, PROT_READ, MAP_ANON | MAP_PRIVATE | MAP_NOCORE, -1, 0);
|
||||
if (base == MAP_FAILED)
|
||||
base = nullptr;
|
||||
ASSERT_MSG(base, "Failed to allocate {:#x} sized region with error {}", size, strerror(errno));
|
||||
#endif
|
||||
return base;
|
||||
}
|
||||
|
||||
void FreeMemoryPages(void* base, [[maybe_unused]] std::size_t size) noexcept {
|
||||
if (auto page = HostPageSize; size % page != 0) {
|
||||
size = AlignUp(size, page);
|
||||
}
|
||||
if (!base)
|
||||
return;
|
||||
#ifdef _WIN32
|
||||
ASSERT(VirtualFree(base, 0, MEM_RELEASE));
|
||||
#else
|
||||
ASSERT(munmap(base, size) == 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
@@ -0,0 +1,194 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/* virtual_buffer.h */
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <bit>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <unistd.h>
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "common/assert.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
#ifdef _WIN32
|
||||
constexpr u64 HostPageSize = 0x1000;
|
||||
constexpr u64 HostPageBits = 12;
|
||||
constexpr u64 HostPageMask = ~(HostPageSize - 1);
|
||||
bool CommitVectorPage(uintptr_t addr, bool write) noexcept;
|
||||
#else
|
||||
const u64 HostPageSize = sysconf(_SC_PAGESIZE);
|
||||
const u64 HostPageBits = std::countr_zero(HostPageSize);
|
||||
const u64 HostPageMask = ~(HostPageSize - 1);
|
||||
#endif
|
||||
|
||||
void* AllocateMemoryPages(std::size_t size) noexcept;
|
||||
void FreeMemoryPages(void* base, std::size_t size) noexcept;
|
||||
|
||||
/// A large page-aligned buffer that has optimized memory usage for zero-writes.
|
||||
template <typename T>
|
||||
requires std::is_trivially_copyable_v<T>
|
||||
class SparseLargeVector final {
|
||||
public:
|
||||
constexpr SparseLargeVector() = default;
|
||||
|
||||
explicit SparseLargeVector(std::size_t count) noexcept
|
||||
: alloc_size{count * sizeof(T)}
|
||||
{
|
||||
base_ptr = static_cast<T*>(AllocateMemoryPages(alloc_size));
|
||||
|
||||
// each item in vector holds information for 64 pages
|
||||
auto denom = HostPageSize * 64;
|
||||
committed_pages = std::vector<std::atomic<u64>>((alloc_size + denom - 1) / denom);
|
||||
}
|
||||
|
||||
~SparseLargeVector() noexcept {
|
||||
FreeMemoryPages(base_ptr, alloc_size);
|
||||
}
|
||||
|
||||
SparseLargeVector(const SparseLargeVector&) = delete;
|
||||
SparseLargeVector& operator=(const SparseLargeVector&) = delete;
|
||||
SparseLargeVector(SparseLargeVector&& other) = delete;
|
||||
SparseLargeVector& operator=(SparseLargeVector&& other) = delete;
|
||||
|
||||
void ResizeAndClear(std::size_t count) noexcept {
|
||||
if (auto const new_size = count * sizeof(T); new_size != alloc_size) {
|
||||
FreeMemoryPages(base_ptr, alloc_size);
|
||||
alloc_size = new_size;
|
||||
base_ptr = static_cast<T*>(AllocateMemoryPages(alloc_size));
|
||||
|
||||
auto denom = HostPageSize * 64;
|
||||
committed_pages = std::vector<std::atomic<u64>>((alloc_size + denom - 1) / denom);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a reference to the value of the requested index and allocates memory if needed.
|
||||
T& GetAndFault(std::size_t index) noexcept {
|
||||
if (index > alloc_size / sizeof(T)) {
|
||||
UNREACHABLE_MSG("Out of bounds RW access on SparseLargeVector @ {}", index);
|
||||
}
|
||||
|
||||
if (!IsCommittedPage(index)) {
|
||||
CommitPage(index);
|
||||
}
|
||||
return base_ptr[index];
|
||||
}
|
||||
|
||||
/// Returns a reference to the value of the requested index if initialized, or will otherwise return a zero-initialized object.
|
||||
const T& GetOrDefault(std::size_t index) const {
|
||||
#ifdef _WIN32
|
||||
if (!IsCommittedPage(index)) {
|
||||
return *reinterpret_cast<const T*>(&default_val);
|
||||
}
|
||||
#endif
|
||||
// On non-Windows, OS page table should optimize this by pointing to a zero page if unallocated.
|
||||
return base_ptr[index];
|
||||
}
|
||||
|
||||
void Set(std::size_t index, const T& value) noexcept {
|
||||
if (index > alloc_size / sizeof(T)) {
|
||||
LOG_CRITICAL(Common_Memory, "Out of bounds write on SparseLargeVector @ {}", index);
|
||||
return;
|
||||
}
|
||||
if (!IsCommittedPage(index))
|
||||
CommitPage(index);
|
||||
base_ptr[index] = value;
|
||||
}
|
||||
|
||||
void ZeroRegion(std::size_t start, std::size_t end_) noexcept {
|
||||
u64 base = reinterpret_cast<u64>(&base_ptr[start]);
|
||||
const u64 end = reinterpret_cast<u64>(&base_ptr[end_]);
|
||||
|
||||
const u64 end_page = AlignUp(base, HostPageSize);
|
||||
const u64 first_size = (std::min)(end_page, end) - base;
|
||||
|
||||
if (IsCommittedPage(start / sizeof(T))) {
|
||||
std::memset(reinterpret_cast<void*>(base), 0, first_size);
|
||||
}
|
||||
|
||||
if (end <= end_page)
|
||||
return;
|
||||
|
||||
base = end_page;
|
||||
|
||||
for (u64 page = base; page < end; page += HostPageSize) {
|
||||
if (!IsCommittedPage((page - reinterpret_cast<u64>(base_ptr)) / sizeof(T))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::memset(reinterpret_cast<void*>(page), 0, (std::min)( HostPageSize, end - page));
|
||||
}
|
||||
}
|
||||
|
||||
constexpr void CommitRegion(size_t index, size_t end_) {
|
||||
const u64 base = static_cast<u64>(index) * sizeof(T);
|
||||
const u64 end = static_cast<u64>(end_) * sizeof(T);
|
||||
|
||||
for (u64 page = AlignDown(base, HostPageSize); page < end; page += HostPageSize) {
|
||||
if (!IsCommittedPage(page / sizeof(T))) {
|
||||
CommitPage(page / sizeof(T));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
constexpr T& GetUnchecked(size_t index) {
|
||||
return base_ptr[index];
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr const T& operator[](std::size_t index) const noexcept {
|
||||
return GetOrDefault(index);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr const T* data() const noexcept {
|
||||
return base_ptr;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr std::size_t size() const noexcept {
|
||||
return alloc_size / sizeof(T);
|
||||
}
|
||||
|
||||
private:
|
||||
[[nodiscard]] constexpr bool IsCommittedPage(std::size_t index) const noexcept {
|
||||
if (index > alloc_size / sizeof(T)) {
|
||||
LOG_CRITICAL(Common_Memory, "Out of bounds access on large vector @ {}", index);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto page = (index * sizeof(T)) >> HostPageBits;
|
||||
auto val = committed_pages[page >> 6].load(std::memory_order_acquire);
|
||||
return (val >> (page & 63)) & 1;
|
||||
}
|
||||
|
||||
constexpr void CommitPage(std::size_t index) noexcept {
|
||||
auto page_index = (index * sizeof(T)) >> HostPageBits;
|
||||
auto page = reinterpret_cast<uintptr_t>(base_ptr + index) & HostPageMask;
|
||||
#if defined(_WIN32)
|
||||
CommitVectorPage(page, true);
|
||||
#else
|
||||
mprotect(reinterpret_cast<void*>(page), HostPageSize, PROT_READ | PROT_WRITE);
|
||||
#endif
|
||||
|
||||
committed_pages[page_index >> 6].fetch_or(1ULL << (page_index & 63), std::memory_order_release);
|
||||
}
|
||||
|
||||
std::size_t alloc_size{};
|
||||
T* base_ptr{};
|
||||
|
||||
std::vector<std::atomic<u64>> committed_pages{};
|
||||
#ifdef _WIN32
|
||||
const std::array<u8, sizeof(T)> default_val{};
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
+89
-713
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: 2014 Tony Wasserka
|
||||
@@ -7,752 +7,128 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __ARM_NEON
|
||||
#include <arm_neon.h>
|
||||
#endif
|
||||
|
||||
#include <cmath>
|
||||
#include <type_traits>
|
||||
|
||||
namespace Common {
|
||||
|
||||
template <typename T>
|
||||
class Vec2;
|
||||
template <typename T>
|
||||
class Vec3;
|
||||
template <typename T>
|
||||
class Vec4;
|
||||
|
||||
template <typename T>
|
||||
class Vec2 {
|
||||
template <typename T, size_t N>
|
||||
class Vec {
|
||||
public:
|
||||
T x{};
|
||||
T y{};
|
||||
std::array<T, N> elems{};
|
||||
|
||||
constexpr Vec2() = default;
|
||||
constexpr Vec2(const T& x_, const T& y_) : x(x_), y(y_) {}
|
||||
constexpr Vec() = default;
|
||||
constexpr Vec(T e0) noexcept : elems{e0} {}
|
||||
constexpr Vec(T e0, T e1) noexcept : elems{e0, e1} {}
|
||||
constexpr Vec(T e0, T e1, T e2) noexcept : elems{e0, e1, e2} {}
|
||||
constexpr Vec(T e0, T e1, T e2, T e4) noexcept : elems{e0, e1, e2, e4} {}
|
||||
//explicit constexpr Vec(const std::initializer_list<T> elems_) noexcept : elems{elems_} {}
|
||||
|
||||
template <typename T2>
|
||||
[[nodiscard]] constexpr Vec2<T2> Cast() const {
|
||||
return Vec2<T2>(static_cast<T2>(x), static_cast<T2>(y));
|
||||
[[nodiscard]] constexpr Vec<decltype(T{} + T{}), N> operator+(const Vec o) const noexcept {
|
||||
Vec<decltype(T{} + T{}), N> r{};
|
||||
for (size_t i = 0; i < N; ++i)
|
||||
r.elems[i] = elems[i] + o.elems[i];
|
||||
return r;
|
||||
}
|
||||
constexpr Vec<T, N> operator+=(const Vec<T, N> o) noexcept { return *this = *this + o; }
|
||||
|
||||
[[nodiscard]] static constexpr Vec2 AssignToAll(const T& f) {
|
||||
return Vec2{f, f};
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr Vec2<decltype(T{} + T{})> operator+(const Vec2& other) const {
|
||||
return {x + other.x, y + other.y};
|
||||
}
|
||||
constexpr Vec2& operator+=(const Vec2& other) {
|
||||
x += other.x;
|
||||
y += other.y;
|
||||
return *this;
|
||||
}
|
||||
[[nodiscard]] constexpr Vec2<decltype(T{} - T{})> operator-(const Vec2& other) const {
|
||||
return {x - other.x, y - other.y};
|
||||
}
|
||||
constexpr Vec2& operator-=(const Vec2& other) {
|
||||
x -= other.x;
|
||||
y -= other.y;
|
||||
return *this;
|
||||
[[nodiscard]] constexpr Vec<decltype(T{} - T{}), N> operator-(const Vec o) const noexcept {
|
||||
Vec<decltype(T{} - T{}), N> r{};
|
||||
for (size_t i = 0; i < N; ++i)
|
||||
r.elems[i] = elems[i] - o.elems[i];
|
||||
return r;
|
||||
}
|
||||
constexpr Vec<T, N> operator-=(const Vec<T, N> o) noexcept { return *this = *this - o; }
|
||||
|
||||
template <typename U = T>
|
||||
[[nodiscard]] constexpr Vec2<std::enable_if_t<std::is_signed_v<U>, U>> operator-() const {
|
||||
return {-x, -y};
|
||||
}
|
||||
[[nodiscard]] constexpr Vec2<decltype(T{} * T{})> operator*(const Vec2& other) const {
|
||||
return {x * other.x, y * other.y};
|
||||
[[nodiscard]] constexpr Vec<std::enable_if_t<std::is_signed_v<U>, U>, N> operator-() const noexcept {
|
||||
Vec<U, N> r{};
|
||||
for (size_t i = 0; i < N; ++i)
|
||||
r.elems[i] = -elems[i];
|
||||
return r;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr Vec<decltype(T{} * T{}), N> operator*(const Vec o) const noexcept {
|
||||
Vec<decltype(T{} * T{}), N> r{};
|
||||
for (size_t i = 0; i < N; ++i)
|
||||
r.elems[i] = elems[i] * o.elems[i];
|
||||
return r;
|
||||
}
|
||||
template <typename V>
|
||||
[[nodiscard]] constexpr Vec2<decltype(T{} * V{})> operator*(const V& f) const {
|
||||
[[nodiscard]] constexpr Vec<decltype(T{} * V{}), N> operator*(const V f) const noexcept {
|
||||
using TV = decltype(T{} * V{});
|
||||
using C = std::common_type_t<T, V>;
|
||||
|
||||
return {
|
||||
static_cast<TV>(static_cast<C>(x) * static_cast<C>(f)),
|
||||
static_cast<TV>(static_cast<C>(y) * static_cast<C>(f)),
|
||||
};
|
||||
Vec<TV, N> r{};
|
||||
for (size_t i = 0; i < N; ++i)
|
||||
r.elems[i] = TV(C(elems[i]) * C(f));
|
||||
return r;
|
||||
}
|
||||
template <typename V>
|
||||
constexpr Vec<T, N> operator*=(const V f) noexcept { return *this = *this * f; }
|
||||
|
||||
template <typename V>
|
||||
constexpr Vec2& operator*=(const V& f) {
|
||||
*this = *this * f;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename V>
|
||||
[[nodiscard]] constexpr Vec2<decltype(T{} / V{})> operator/(const V& f) const {
|
||||
[[nodiscard]] constexpr Vec<decltype(T{} / V{}), N> operator/(const V f) const noexcept {
|
||||
using TV = decltype(T{} / V{});
|
||||
using C = std::common_type_t<T, V>;
|
||||
|
||||
return {
|
||||
static_cast<TV>(static_cast<C>(x) / static_cast<C>(f)),
|
||||
static_cast<TV>(static_cast<C>(y) / static_cast<C>(f)),
|
||||
};
|
||||
Vec<TV, N> r{};
|
||||
for (size_t i = 0; i < N; ++i)
|
||||
r.elems[i] = TV(C(elems[i]) / C(f));
|
||||
return r;
|
||||
}
|
||||
|
||||
template <typename V>
|
||||
constexpr Vec2& operator/=(const V& f) {
|
||||
*this = *this / f;
|
||||
return *this;
|
||||
}
|
||||
constexpr Vec<T, N> operator/=(const V f) noexcept { return *this = *this / f; }
|
||||
|
||||
[[nodiscard]] constexpr T Length2() const {
|
||||
return x * x + y * y;
|
||||
[[nodiscard]] constexpr T Length2() const noexcept {
|
||||
T r{};
|
||||
for (size_t i = 0; i < N; ++i)
|
||||
r += elems[i] * elems[i];
|
||||
return r;
|
||||
}
|
||||
|
||||
// Only implemented for T=float
|
||||
[[nodiscard]] float Length() const;
|
||||
[[nodiscard]] float Normalize(); // returns the previous length, which is often useful
|
||||
[[nodiscard]] T Length() const { return T(std::sqrt(float(Length2()))); }
|
||||
[[nodiscard]] Vec<T, N> Normalized() const { return *this / Length(); }
|
||||
[[nodiscard]] constexpr T& operator[](std::size_t i) noexcept { return elems[i]; }
|
||||
[[nodiscard]] constexpr const T& operator[](std::size_t i) const noexcept { return elems[i]; }
|
||||
|
||||
[[nodiscard]] constexpr T& operator[](std::size_t i) {
|
||||
return *((&x) + i);
|
||||
}
|
||||
[[nodiscard]] constexpr const T& operator[](std::size_t i) const {
|
||||
return *((&x) + i);
|
||||
}
|
||||
[[nodiscard]] std::array<decltype(-T{}), 16> ToMatrix() const {
|
||||
const T x2 = elems[0] * elems[0];
|
||||
const T y2 = elems[1] * elems[1];
|
||||
const T z2 = elems[2] * elems[2];
|
||||
|
||||
constexpr void SetZero() {
|
||||
x = 0;
|
||||
y = 0;
|
||||
}
|
||||
|
||||
// Common aliases: UV (texel coordinates), ST (texture coordinates)
|
||||
[[nodiscard]] constexpr T& u() {
|
||||
return x;
|
||||
}
|
||||
[[nodiscard]] constexpr T& v() {
|
||||
return y;
|
||||
}
|
||||
[[nodiscard]] constexpr T& s() {
|
||||
return x;
|
||||
}
|
||||
[[nodiscard]] constexpr T& t() {
|
||||
return y;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr const T& u() const {
|
||||
return x;
|
||||
}
|
||||
[[nodiscard]] constexpr const T& v() const {
|
||||
return y;
|
||||
}
|
||||
[[nodiscard]] constexpr const T& s() const {
|
||||
return x;
|
||||
}
|
||||
[[nodiscard]] constexpr const T& t() const {
|
||||
return y;
|
||||
}
|
||||
|
||||
// swizzlers - create a subvector of specific components
|
||||
[[nodiscard]] constexpr Vec2 yx() const {
|
||||
return Vec2(y, x);
|
||||
}
|
||||
[[nodiscard]] constexpr Vec2 vu() const {
|
||||
return Vec2(y, x);
|
||||
}
|
||||
[[nodiscard]] constexpr Vec2 ts() const {
|
||||
return Vec2(y, x);
|
||||
const T xy = elems[0] * elems[1];
|
||||
const T wz = elems[3] * elems[2];
|
||||
const T xz = elems[0] * elems[2];
|
||||
const T wy = elems[3] * elems[1];
|
||||
const T yz = elems[1] * elems[2];
|
||||
const T wx = elems[3] * elems[0];
|
||||
return {
|
||||
1.0f - 2.0f * (y2 + z2),
|
||||
2.0f * (xy + wz),
|
||||
2.0f * (xz - wy),
|
||||
0.0f,
|
||||
2.0f * (xy - wz),
|
||||
1.0f - 2.0f * (x2 + z2),
|
||||
2.0f * (yz + wx),
|
||||
0.0f,
|
||||
2.0f * (xz + wy),
|
||||
2.0f * (yz - wx),
|
||||
1.0f - 2.0f * (x2 + y2),
|
||||
0.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
1.0f
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename V>
|
||||
[[nodiscard]] constexpr Vec2<T> operator*(const V& f, const Vec2<T>& vec) {
|
||||
template <typename T, size_t N, typename V>
|
||||
[[nodiscard]] constexpr Vec<T, N> operator*(const V f, const Vec<T, N> v) noexcept {
|
||||
using C = std::common_type_t<T, V>;
|
||||
|
||||
return Vec2<T>(static_cast<T>(static_cast<C>(f) * static_cast<C>(vec.x)),
|
||||
static_cast<T>(static_cast<C>(f) * static_cast<C>(vec.y)));
|
||||
}
|
||||
|
||||
using Vec2f = Vec2<float>;
|
||||
|
||||
template <>
|
||||
inline float Vec2<float>::Length() const {
|
||||
return std::sqrt(x * x + y * y);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline float Vec2<float>::Normalize() {
|
||||
float length = Length();
|
||||
*this /= length;
|
||||
return length;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
class Vec3 {
|
||||
public:
|
||||
T x{};
|
||||
T y{};
|
||||
T z{};
|
||||
|
||||
constexpr Vec3() = default;
|
||||
constexpr Vec3(const T& x_, const T& y_, const T& z_) : x(x_), y(y_), z(z_) {}
|
||||
|
||||
template <typename T2>
|
||||
[[nodiscard]] constexpr Vec3<T2> Cast() const {
|
||||
return Vec3<T2>(static_cast<T2>(x), static_cast<T2>(y), static_cast<T2>(z));
|
||||
}
|
||||
|
||||
[[nodiscard]] static constexpr Vec3 AssignToAll(const T& f) {
|
||||
return Vec3(f, f, f);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr Vec3<decltype(T{} + T{})> operator+(const Vec3& other) const {
|
||||
return {x + other.x, y + other.y, z + other.z};
|
||||
}
|
||||
|
||||
constexpr Vec3& operator+=(const Vec3& other) {
|
||||
x += other.x;
|
||||
y += other.y;
|
||||
z += other.z;
|
||||
return *this;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr Vec3<decltype(T{} - T{})> operator-(const Vec3& other) const {
|
||||
return {x - other.x, y - other.y, z - other.z};
|
||||
}
|
||||
|
||||
constexpr Vec3& operator-=(const Vec3& other) {
|
||||
x -= other.x;
|
||||
y -= other.y;
|
||||
z -= other.z;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename U = T>
|
||||
[[nodiscard]] constexpr Vec3<std::enable_if_t<std::is_signed_v<U>, U>> operator-() const {
|
||||
return {-x, -y, -z};
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr Vec3<decltype(T{} * T{})> operator*(const Vec3& other) const {
|
||||
return {x * other.x, y * other.y, z * other.z};
|
||||
}
|
||||
|
||||
template <typename V>
|
||||
[[nodiscard]] constexpr Vec3<decltype(T{} * V{})> operator*(const V& f) const {
|
||||
using TV = decltype(T{} * V{});
|
||||
using C = std::common_type_t<T, V>;
|
||||
|
||||
return {
|
||||
static_cast<TV>(static_cast<C>(x) * static_cast<C>(f)),
|
||||
static_cast<TV>(static_cast<C>(y) * static_cast<C>(f)),
|
||||
static_cast<TV>(static_cast<C>(z) * static_cast<C>(f)),
|
||||
};
|
||||
}
|
||||
|
||||
template <typename V>
|
||||
constexpr Vec3& operator*=(const V& f) {
|
||||
*this = *this * f;
|
||||
return *this;
|
||||
}
|
||||
template <typename V>
|
||||
[[nodiscard]] constexpr Vec3<decltype(T{} / V{})> operator/(const V& f) const {
|
||||
using TV = decltype(T{} / V{});
|
||||
using C = std::common_type_t<T, V>;
|
||||
|
||||
return {
|
||||
static_cast<TV>(static_cast<C>(x) / static_cast<C>(f)),
|
||||
static_cast<TV>(static_cast<C>(y) / static_cast<C>(f)),
|
||||
static_cast<TV>(static_cast<C>(z) / static_cast<C>(f)),
|
||||
};
|
||||
}
|
||||
|
||||
template <typename V>
|
||||
constexpr Vec3& operator/=(const V& f) {
|
||||
*this = *this / f;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void RotateFromOrigin(float roll, float pitch, float yaw) {
|
||||
float temp = y;
|
||||
y = std::cos(roll) * y - std::sin(roll) * z;
|
||||
z = std::sin(roll) * temp + std::cos(roll) * z;
|
||||
|
||||
temp = x;
|
||||
x = std::cos(pitch) * x + std::sin(pitch) * z;
|
||||
z = -std::sin(pitch) * temp + std::cos(pitch) * z;
|
||||
|
||||
temp = x;
|
||||
x = std::cos(yaw) * x - std::sin(yaw) * y;
|
||||
y = std::sin(yaw) * temp + std::cos(yaw) * y;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr T Length2() const {
|
||||
return x * x + y * y + z * z;
|
||||
}
|
||||
|
||||
// Only implemented for T=float
|
||||
[[nodiscard]] float Length() const;
|
||||
[[nodiscard]] Vec3 Normalized() const;
|
||||
[[nodiscard]] float Normalize(); // returns the previous length, which is often useful
|
||||
|
||||
[[nodiscard]] constexpr T& operator[](std::size_t i) {
|
||||
return *((&x) + i);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr const T& operator[](std::size_t i) const {
|
||||
return *((&x) + i);
|
||||
}
|
||||
|
||||
constexpr void SetZero() {
|
||||
x = 0;
|
||||
y = 0;
|
||||
z = 0;
|
||||
}
|
||||
|
||||
// Common aliases: UVW (texel coordinates), RGB (colors), STQ (texture coordinates)
|
||||
[[nodiscard]] constexpr T& u() {
|
||||
return x;
|
||||
}
|
||||
[[nodiscard]] constexpr T& v() {
|
||||
return y;
|
||||
}
|
||||
[[nodiscard]] constexpr T& w() {
|
||||
return z;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr T& r() {
|
||||
return x;
|
||||
}
|
||||
[[nodiscard]] constexpr T& g() {
|
||||
return y;
|
||||
}
|
||||
[[nodiscard]] constexpr T& b() {
|
||||
return z;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr T& s() {
|
||||
return x;
|
||||
}
|
||||
[[nodiscard]] constexpr T& t() {
|
||||
return y;
|
||||
}
|
||||
[[nodiscard]] constexpr T& q() {
|
||||
return z;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr const T& u() const {
|
||||
return x;
|
||||
}
|
||||
[[nodiscard]] constexpr const T& v() const {
|
||||
return y;
|
||||
}
|
||||
[[nodiscard]] constexpr const T& w() const {
|
||||
return z;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr const T& r() const {
|
||||
return x;
|
||||
}
|
||||
[[nodiscard]] constexpr const T& g() const {
|
||||
return y;
|
||||
}
|
||||
[[nodiscard]] constexpr const T& b() const {
|
||||
return z;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr const T& s() const {
|
||||
return x;
|
||||
}
|
||||
[[nodiscard]] constexpr const T& t() const {
|
||||
return y;
|
||||
}
|
||||
[[nodiscard]] constexpr const T& q() const {
|
||||
return z;
|
||||
}
|
||||
|
||||
// swizzlers - create a subvector of specific components
|
||||
// e.g. Vec2 uv() { return Vec2(x,y); }
|
||||
// _DEFINE_SWIZZLER2 defines a single such function, DEFINE_SWIZZLER2 defines all of them for all
|
||||
// component names (x<->r) and permutations (xy<->yx)
|
||||
#define _DEFINE_SWIZZLER2(a, b, name) \
|
||||
[[nodiscard]] constexpr Vec2<T> name() const { return Vec2<T>(a, b); }
|
||||
#define DEFINE_SWIZZLER2(a, b, a2, b2, a3, b3, a4, b4) \
|
||||
_DEFINE_SWIZZLER2(a, b, a##b); \
|
||||
_DEFINE_SWIZZLER2(a, b, a2##b2); \
|
||||
_DEFINE_SWIZZLER2(a, b, a3##b3); \
|
||||
_DEFINE_SWIZZLER2(a, b, a4##b4); \
|
||||
_DEFINE_SWIZZLER2(b, a, b##a); \
|
||||
_DEFINE_SWIZZLER2(b, a, b2##a2); \
|
||||
_DEFINE_SWIZZLER2(b, a, b3##a3); \
|
||||
_DEFINE_SWIZZLER2(b, a, b4##a4)
|
||||
|
||||
DEFINE_SWIZZLER2(x, y, r, g, u, v, s, t);
|
||||
DEFINE_SWIZZLER2(x, z, r, b, u, w, s, q);
|
||||
DEFINE_SWIZZLER2(y, z, g, b, v, w, t, q);
|
||||
#undef DEFINE_SWIZZLER2
|
||||
#undef _DEFINE_SWIZZLER2
|
||||
};
|
||||
|
||||
template <typename T, typename V>
|
||||
[[nodiscard]] constexpr Vec3<T> operator*(const V& f, const Vec3<T>& vec) {
|
||||
using C = std::common_type_t<T, V>;
|
||||
|
||||
return Vec3<T>(static_cast<T>(static_cast<C>(f) * static_cast<C>(vec.x)),
|
||||
static_cast<T>(static_cast<C>(f) * static_cast<C>(vec.y)),
|
||||
static_cast<T>(static_cast<C>(f) * static_cast<C>(vec.z)));
|
||||
}
|
||||
|
||||
template <>
|
||||
inline float Vec3<float>::Length() const {
|
||||
return std::sqrt(x * x + y * y + z * z);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline Vec3<float> Vec3<float>::Normalized() const {
|
||||
return *this / Length();
|
||||
}
|
||||
|
||||
template <>
|
||||
inline float Vec3<float>::Normalize() {
|
||||
float length = Length();
|
||||
*this /= length;
|
||||
return length;
|
||||
}
|
||||
|
||||
using Vec3f = Vec3<float>;
|
||||
|
||||
template <typename T>
|
||||
class Vec4 {
|
||||
public:
|
||||
T x{};
|
||||
T y{};
|
||||
T z{};
|
||||
T w{};
|
||||
|
||||
constexpr Vec4() = default;
|
||||
constexpr Vec4(const T& x_, const T& y_, const T& z_, const T& w_)
|
||||
: x(x_), y(y_), z(z_), w(w_) {}
|
||||
|
||||
template <typename T2>
|
||||
[[nodiscard]] constexpr Vec4<T2> Cast() const {
|
||||
return Vec4<T2>(static_cast<T2>(x), static_cast<T2>(y), static_cast<T2>(z),
|
||||
static_cast<T2>(w));
|
||||
}
|
||||
|
||||
[[nodiscard]] static constexpr Vec4 AssignToAll(const T& f) {
|
||||
return Vec4(f, f, f, f);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr Vec4<decltype(T{} + T{})> operator+(const Vec4& other) const {
|
||||
return {x + other.x, y + other.y, z + other.z, w + other.w};
|
||||
}
|
||||
|
||||
constexpr Vec4& operator+=(const Vec4& other) {
|
||||
x += other.x;
|
||||
y += other.y;
|
||||
z += other.z;
|
||||
w += other.w;
|
||||
return *this;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr Vec4<decltype(T{} - T{})> operator-(const Vec4& other) const {
|
||||
return {x - other.x, y - other.y, z - other.z, w - other.w};
|
||||
}
|
||||
|
||||
constexpr Vec4& operator-=(const Vec4& other) {
|
||||
x -= other.x;
|
||||
y -= other.y;
|
||||
z -= other.z;
|
||||
w -= other.w;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename U = T>
|
||||
[[nodiscard]] constexpr Vec4<std::enable_if_t<std::is_signed_v<U>, U>> operator-() const {
|
||||
return {-x, -y, -z, -w};
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr Vec4<decltype(T{} * T{})> operator*(const Vec4& other) const {
|
||||
return {x * other.x, y * other.y, z * other.z, w * other.w};
|
||||
}
|
||||
|
||||
template <typename V>
|
||||
[[nodiscard]] constexpr Vec4<decltype(T{} * V{})> operator*(const V& f) const {
|
||||
using TV = decltype(T{} * V{});
|
||||
using C = std::common_type_t<T, V>;
|
||||
|
||||
return {
|
||||
static_cast<TV>(static_cast<C>(x) * static_cast<C>(f)),
|
||||
static_cast<TV>(static_cast<C>(y) * static_cast<C>(f)),
|
||||
static_cast<TV>(static_cast<C>(z) * static_cast<C>(f)),
|
||||
static_cast<TV>(static_cast<C>(w) * static_cast<C>(f)),
|
||||
};
|
||||
}
|
||||
|
||||
template <typename V>
|
||||
constexpr Vec4& operator*=(const V& f) {
|
||||
*this = *this * f;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename V>
|
||||
[[nodiscard]] constexpr Vec4<decltype(T{} / V{})> operator/(const V& f) const {
|
||||
using TV = decltype(T{} / V{});
|
||||
using C = std::common_type_t<T, V>;
|
||||
|
||||
return {
|
||||
static_cast<TV>(static_cast<C>(x) / static_cast<C>(f)),
|
||||
static_cast<TV>(static_cast<C>(y) / static_cast<C>(f)),
|
||||
static_cast<TV>(static_cast<C>(z) / static_cast<C>(f)),
|
||||
static_cast<TV>(static_cast<C>(w) / static_cast<C>(f)),
|
||||
};
|
||||
}
|
||||
|
||||
template <typename V>
|
||||
constexpr Vec4& operator/=(const V& f) {
|
||||
*this = *this / f;
|
||||
return *this;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr T Length2() const {
|
||||
return x * x + y * y + z * z + w * w;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr T& operator[](std::size_t i) {
|
||||
return *((&x) + i);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr const T& operator[](std::size_t i) const {
|
||||
return *((&x) + i);
|
||||
}
|
||||
|
||||
constexpr void SetZero() {
|
||||
x = 0;
|
||||
y = 0;
|
||||
z = 0;
|
||||
w = 0;
|
||||
}
|
||||
|
||||
// Common alias: RGBA (colors)
|
||||
[[nodiscard]] constexpr T& r() {
|
||||
return x;
|
||||
}
|
||||
[[nodiscard]] constexpr T& g() {
|
||||
return y;
|
||||
}
|
||||
[[nodiscard]] constexpr T& b() {
|
||||
return z;
|
||||
}
|
||||
[[nodiscard]] constexpr T& a() {
|
||||
return w;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr const T& r() const {
|
||||
return x;
|
||||
}
|
||||
[[nodiscard]] constexpr const T& g() const {
|
||||
return y;
|
||||
}
|
||||
[[nodiscard]] constexpr const T& b() const {
|
||||
return z;
|
||||
}
|
||||
[[nodiscard]] constexpr const T& a() const {
|
||||
return w;
|
||||
}
|
||||
|
||||
// Swizzlers - Create a subvector of specific components
|
||||
// e.g. Vec2 uv() { return Vec2(x,y); }
|
||||
|
||||
// _DEFINE_SWIZZLER2 defines a single such function
|
||||
// DEFINE_SWIZZLER2_COMP1 defines one-component functions for all component names (x<->r)
|
||||
// DEFINE_SWIZZLER2_COMP2 defines two component functions for all component names (x<->r) and
|
||||
// permutations (xy<->yx)
|
||||
#define _DEFINE_SWIZZLER2(a, b, name) \
|
||||
[[nodiscard]] constexpr Vec2<T> name() const { return Vec2<T>(a, b); }
|
||||
#define DEFINE_SWIZZLER2_COMP1(a, a2) \
|
||||
_DEFINE_SWIZZLER2(a, a, a##a); \
|
||||
_DEFINE_SWIZZLER2(a, a, a2##a2)
|
||||
#define DEFINE_SWIZZLER2_COMP2(a, b, a2, b2) \
|
||||
_DEFINE_SWIZZLER2(a, b, a##b); \
|
||||
_DEFINE_SWIZZLER2(a, b, a2##b2); \
|
||||
_DEFINE_SWIZZLER2(b, a, b##a); \
|
||||
_DEFINE_SWIZZLER2(b, a, b2##a2)
|
||||
|
||||
DEFINE_SWIZZLER2_COMP2(x, y, r, g);
|
||||
DEFINE_SWIZZLER2_COMP2(x, z, r, b);
|
||||
DEFINE_SWIZZLER2_COMP2(x, w, r, a);
|
||||
DEFINE_SWIZZLER2_COMP2(y, z, g, b);
|
||||
DEFINE_SWIZZLER2_COMP2(y, w, g, a);
|
||||
DEFINE_SWIZZLER2_COMP2(z, w, b, a);
|
||||
DEFINE_SWIZZLER2_COMP1(x, r);
|
||||
DEFINE_SWIZZLER2_COMP1(y, g);
|
||||
DEFINE_SWIZZLER2_COMP1(z, b);
|
||||
DEFINE_SWIZZLER2_COMP1(w, a);
|
||||
#undef DEFINE_SWIZZLER2_COMP1
|
||||
#undef DEFINE_SWIZZLER2_COMP2
|
||||
#undef _DEFINE_SWIZZLER2
|
||||
|
||||
#define _DEFINE_SWIZZLER3(a, b, c, name) \
|
||||
[[nodiscard]] constexpr Vec3<T> name() const { return Vec3<T>(a, b, c); }
|
||||
#define DEFINE_SWIZZLER3_COMP1(a, a2) \
|
||||
_DEFINE_SWIZZLER3(a, a, a, a##a##a); \
|
||||
_DEFINE_SWIZZLER3(a, a, a, a2##a2##a2)
|
||||
#define DEFINE_SWIZZLER3_COMP3(a, b, c, a2, b2, c2) \
|
||||
_DEFINE_SWIZZLER3(a, b, c, a##b##c); \
|
||||
_DEFINE_SWIZZLER3(a, c, b, a##c##b); \
|
||||
_DEFINE_SWIZZLER3(b, a, c, b##a##c); \
|
||||
_DEFINE_SWIZZLER3(b, c, a, b##c##a); \
|
||||
_DEFINE_SWIZZLER3(c, a, b, c##a##b); \
|
||||
_DEFINE_SWIZZLER3(c, b, a, c##b##a); \
|
||||
_DEFINE_SWIZZLER3(a, b, c, a2##b2##c2); \
|
||||
_DEFINE_SWIZZLER3(a, c, b, a2##c2##b2); \
|
||||
_DEFINE_SWIZZLER3(b, a, c, b2##a2##c2); \
|
||||
_DEFINE_SWIZZLER3(b, c, a, b2##c2##a2); \
|
||||
_DEFINE_SWIZZLER3(c, a, b, c2##a2##b2); \
|
||||
_DEFINE_SWIZZLER3(c, b, a, c2##b2##a2)
|
||||
|
||||
DEFINE_SWIZZLER3_COMP3(x, y, z, r, g, b);
|
||||
DEFINE_SWIZZLER3_COMP3(x, y, w, r, g, a);
|
||||
DEFINE_SWIZZLER3_COMP3(x, z, w, r, b, a);
|
||||
DEFINE_SWIZZLER3_COMP3(y, z, w, g, b, a);
|
||||
DEFINE_SWIZZLER3_COMP1(x, r);
|
||||
DEFINE_SWIZZLER3_COMP1(y, g);
|
||||
DEFINE_SWIZZLER3_COMP1(z, b);
|
||||
DEFINE_SWIZZLER3_COMP1(w, a);
|
||||
#undef DEFINE_SWIZZLER3_COMP1
|
||||
#undef DEFINE_SWIZZLER3_COMP3
|
||||
#undef _DEFINE_SWIZZLER3
|
||||
};
|
||||
|
||||
template <typename T, typename V>
|
||||
[[nodiscard]] constexpr Vec4<decltype(V{} * T{})> operator*(const V& f, const Vec4<T>& vec) {
|
||||
using TV = decltype(V{} * T{});
|
||||
using C = std::common_type_t<T, V>;
|
||||
|
||||
return {
|
||||
static_cast<TV>(static_cast<C>(f) * static_cast<C>(vec.x)),
|
||||
static_cast<TV>(static_cast<C>(f) * static_cast<C>(vec.y)),
|
||||
static_cast<TV>(static_cast<C>(f) * static_cast<C>(vec.z)),
|
||||
static_cast<TV>(static_cast<C>(f) * static_cast<C>(vec.w)),
|
||||
};
|
||||
}
|
||||
|
||||
using Vec4f = Vec4<float>;
|
||||
|
||||
template <typename T>
|
||||
constexpr decltype(T{} * T{} + T{} * T{}) Dot(const Vec2<T>& a, const Vec2<T>& b) {
|
||||
return a.x * b.x + a.y * b.y;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] constexpr decltype(T{} * T{} + T{} * T{}) Dot(const Vec3<T>& a, const Vec3<T>& b) {
|
||||
return a.x * b.x + a.y * b.y + a.z * b.z;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] constexpr decltype(T{} * T{} + T{} * T{}) Dot(const Vec4<T>& a, const Vec4<T>& b) {
|
||||
return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w;
|
||||
}
|
||||
|
||||
template <>
|
||||
[[nodiscard]] inline float Dot(const Vec4<float>& a, const Vec4<float>& b) {
|
||||
#ifdef __ARM_NEON
|
||||
float32x4_t va = vld1q_f32(&a.x);
|
||||
float32x4_t vb = vld1q_f32(&b.x);
|
||||
float32x4_t result = vmulq_f32(va, vb);
|
||||
#if defined(__aarch64__) // Use vaddvq_f32 in ARMv8 architectures
|
||||
return vaddvq_f32(result);
|
||||
#else // Use manual addition for older architectures
|
||||
float32x2_t sum2 = vadd_f32(vget_high_f32(result), vget_low_f32(result));
|
||||
return vget_lane_f32(vpadd_f32(sum2, sum2), 0);
|
||||
#endif
|
||||
#else
|
||||
return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w;
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] constexpr Vec3<decltype(T{} * T{} - T{} * T{})> Cross(const Vec3<T>& a,
|
||||
const Vec3<T>& b) {
|
||||
return {a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x};
|
||||
}
|
||||
|
||||
// linear interpolation via float: 0.0=begin, 1.0=end
|
||||
template <typename X>
|
||||
[[nodiscard]] constexpr decltype(X{} * float{} + X{} * float{}) Lerp(const X& begin, const X& end,
|
||||
const float t) {
|
||||
return begin * (1.f - t) + end * t;
|
||||
}
|
||||
|
||||
// linear interpolation via int: 0=begin, base=end
|
||||
template <typename X, int base>
|
||||
[[nodiscard]] constexpr decltype((X{} * int{} + X{} * int{}) / base) LerpInt(const X& begin,
|
||||
const X& end,
|
||||
const int t) {
|
||||
return (begin * (base - t) + end * t) / base;
|
||||
}
|
||||
|
||||
// bilinear interpolation. s is for interpolating x00-x01 and x10-x11, and t is for the second
|
||||
// interpolation.
|
||||
template <typename X>
|
||||
[[nodiscard]] constexpr auto BilinearInterp(const X& x00, const X& x01, const X& x10, const X& x11,
|
||||
const float s, const float t) {
|
||||
auto y0 = Lerp(x00, x01, s);
|
||||
auto y1 = Lerp(x10, x11, s);
|
||||
return Lerp(y0, y1, t);
|
||||
}
|
||||
|
||||
// Utility vector factories
|
||||
template <typename T>
|
||||
[[nodiscard]] constexpr Vec2<T> MakeVec(const T& x, const T& y) {
|
||||
return Vec2<T>{x, y};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] constexpr Vec3<T> MakeVec(const T& x, const T& y, const T& z) {
|
||||
return Vec3<T>{x, y, z};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] constexpr Vec4<T> MakeVec(const T& x, const T& y, const Vec2<T>& zw) {
|
||||
return MakeVec(x, y, zw[0], zw[1]);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] constexpr Vec3<T> MakeVec(const Vec2<T>& xy, const T& z) {
|
||||
return MakeVec(xy[0], xy[1], z);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] constexpr Vec3<T> MakeVec(const T& x, const Vec2<T>& yz) {
|
||||
return MakeVec(x, yz[0], yz[1]);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] constexpr Vec4<T> MakeVec(const T& x, const T& y, const T& z, const T& w) {
|
||||
return Vec4<T>{x, y, z, w};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] constexpr Vec4<T> MakeVec(const Vec2<T>& xy, const T& z, const T& w) {
|
||||
return MakeVec(xy[0], xy[1], z, w);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] constexpr Vec4<T> MakeVec(const T& x, const Vec2<T>& yz, const T& w) {
|
||||
return MakeVec(x, yz[0], yz[1], w);
|
||||
}
|
||||
|
||||
// NOTE: This has priority over "Vec2<Vec2<T>> MakeVec(const Vec2<T>& x, const Vec2<T>& y)".
|
||||
// Even if someone wanted to use an odd object like Vec2<Vec2<T>>, the compiler would error
|
||||
// out soon enough due to misuse of the returned structure.
|
||||
template <typename T>
|
||||
[[nodiscard]] constexpr Vec4<T> MakeVec(const Vec2<T>& xy, const Vec2<T>& zw) {
|
||||
return MakeVec(xy[0], xy[1], zw[0], zw[1]);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] constexpr Vec4<T> MakeVec(const Vec3<T>& xyz, const T& w) {
|
||||
return MakeVec(xyz[0], xyz[1], xyz[2], w);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] constexpr Vec4<T> MakeVec(const T& x, const Vec3<T>& yzw) {
|
||||
return MakeVec(x, yzw[0], yzw[1], yzw[2]);
|
||||
Vec<T, N> r{};
|
||||
for (size_t i = 0; i < N; ++i)
|
||||
r.elems[i] = T(C(f) * C(v.elems[i]));
|
||||
return r;
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
// 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
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/virtual_buffer.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
void* AllocateMemoryPages(std::size_t size) noexcept {
|
||||
#ifdef _WIN32
|
||||
void* base = VirtualAlloc(nullptr, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
||||
if (base == nullptr) {
|
||||
// Probably failing to reserve is less likely than failing to commit
|
||||
base = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_READWRITE);
|
||||
}
|
||||
#else
|
||||
void* base = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
|
||||
if (base == MAP_FAILED)
|
||||
base = nullptr;
|
||||
#endif
|
||||
ASSERT(base);
|
||||
return base;
|
||||
}
|
||||
|
||||
void FreeMemoryPages(void* base, [[maybe_unused]] std::size_t size) noexcept {
|
||||
if (!base)
|
||||
return;
|
||||
#ifdef _WIN32
|
||||
ASSERT(VirtualFree(base, 0, MEM_RELEASE));
|
||||
#else
|
||||
ASSERT(munmap(base, size) == 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
@@ -1,84 +0,0 @@
|
||||
// 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
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace Common {
|
||||
|
||||
void* AllocateMemoryPages(std::size_t size) noexcept;
|
||||
void FreeMemoryPages(void* base, std::size_t size) noexcept;
|
||||
|
||||
template <typename T>
|
||||
class VirtualBuffer final {
|
||||
public:
|
||||
// TODO: Uncomment this and change Common::PageTable::PageInfo to be trivially constructible
|
||||
// using std::atomic_ref once libc++ has support for it
|
||||
// static_assert(
|
||||
// std::is_trivially_constructible_v<T>,
|
||||
// "T must be trivially constructible, as non-trivial constructors will not be executed "
|
||||
// "with the current allocator");
|
||||
|
||||
constexpr VirtualBuffer() = default;
|
||||
explicit VirtualBuffer(std::size_t count) noexcept
|
||||
: alloc_size{count * sizeof(T)}
|
||||
{
|
||||
base_ptr = reinterpret_cast<T*>(AllocateMemoryPages(alloc_size));
|
||||
}
|
||||
|
||||
~VirtualBuffer() noexcept {
|
||||
FreeMemoryPages(base_ptr, alloc_size);
|
||||
}
|
||||
|
||||
VirtualBuffer(const VirtualBuffer&) = delete;
|
||||
VirtualBuffer& operator=(const VirtualBuffer&) = delete;
|
||||
|
||||
VirtualBuffer(VirtualBuffer&& other) noexcept
|
||||
: alloc_size{std::exchange(other.alloc_size, 0)}
|
||||
, base_ptr{std::exchange(other.base_ptr, nullptr)}
|
||||
{}
|
||||
|
||||
VirtualBuffer& operator=(VirtualBuffer&& other) noexcept {
|
||||
alloc_size = std::exchange(other.alloc_size, 0);
|
||||
base_ptr = std::exchange(other.base_ptr, nullptr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void resize(std::size_t count) noexcept {
|
||||
if (auto const new_size = count * sizeof(T); new_size != alloc_size) {
|
||||
FreeMemoryPages(base_ptr, alloc_size);
|
||||
alloc_size = new_size;
|
||||
base_ptr = reinterpret_cast<T*>(AllocateMemoryPages(alloc_size));
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr const T& operator[](std::size_t index) const noexcept {
|
||||
return base_ptr[index];
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr T& operator[](std::size_t index) noexcept {
|
||||
return base_ptr[index];
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr T* data() noexcept {
|
||||
return base_ptr;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr const T* data() const noexcept {
|
||||
return base_ptr;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr std::size_t size() const noexcept {
|
||||
return alloc_size / sizeof(T);
|
||||
}
|
||||
|
||||
private:
|
||||
std::size_t alloc_size{};
|
||||
T* base_ptr{};
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
@@ -711,8 +711,6 @@ add_library(core STATIC
|
||||
hle/service/kernel_helpers.h
|
||||
hle/service/lbl/lbl.cpp
|
||||
hle/service/lbl/lbl.h
|
||||
hle/service/ldn/client_process_monitor.cpp
|
||||
hle/service/ldn/client_process_monitor.h
|
||||
hle/service/ldn/lan_discovery.cpp
|
||||
hle/service/ldn/lan_discovery.h
|
||||
hle/service/ldn/ldn.cpp
|
||||
@@ -832,8 +830,6 @@ add_library(core STATIC
|
||||
hle/service/ns/system_update_control.h
|
||||
hle/service/ns/system_update_interface.cpp
|
||||
hle/service/ns/system_update_interface.h
|
||||
hle/service/ns/vulnerability_manager_interface.cpp
|
||||
hle/service/ns/vulnerability_manager_interface.h
|
||||
hle/service/nvdrv/core/container.cpp
|
||||
hle/service/nvdrv/core/container.h
|
||||
hle/service/nvdrv/core/heap_mapper.cpp
|
||||
@@ -1067,8 +1063,6 @@ add_library(core STATIC
|
||||
hle/service/sockets/sockets.h
|
||||
hle/service/sockets/sockets_translate.cpp
|
||||
hle/service/sockets/sockets_translate.h
|
||||
hle/service/spl/csrng.cpp
|
||||
hle/service/spl/csrng.h
|
||||
hle/service/spl/spl.cpp
|
||||
hle/service/spl/spl.h
|
||||
hle/service/spl/spl_module.cpp
|
||||
@@ -1083,8 +1077,6 @@ add_library(core STATIC
|
||||
hle/service/ssl/ssl_types.h
|
||||
hle/service/tma/tma.cpp
|
||||
hle/service/tma/tma.h
|
||||
hle/service/ulsf/ulsf.cpp
|
||||
hle/service/ulsf/ulsf.h
|
||||
hle/service/usb/usb.cpp
|
||||
hle/service/usb/usb.h
|
||||
hle/service/vi/application_display_service.cpp
|
||||
@@ -1249,7 +1241,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
|
||||
|
||||
@@ -172,12 +172,12 @@ void ArmDynarmic32::MakeJit(Common::PageTable* page_table) {
|
||||
if (page_table) {
|
||||
constexpr size_t PageBits = 12;
|
||||
constexpr size_t NumPageTableEntries = 1 << (32 - PageBits);
|
||||
constexpr size_t PageLog2Stride = 5;
|
||||
static_assert(1 << PageLog2Stride == sizeof(Common::PageTable::PageEntryData));
|
||||
|
||||
config.page_table = reinterpret_cast<std::array<std::uint8_t*, NumPageTableEntries>*>(page_table->entries.data());
|
||||
config.page_table_pointer_mask_bits = Common::PageTable::ATTRIBUTE_BITS;
|
||||
config.page_table_log2_stride = PageLog2Stride;
|
||||
// Dynarmic will not write to the page table, const_cast is safe here
|
||||
config.page_table = reinterpret_cast<std::array<std::uint8_t*, NumPageTableEntries>*>(
|
||||
const_cast<Common::PageTable::PageEntryData*>(page_table->entries.data()));
|
||||
config.page_table_pointer_mask = Common::PageTable::ATTRIBUTE_MASK;
|
||||
config.page_table_marked_bit = 0;
|
||||
config.absolute_offset_page_table = true;
|
||||
config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128;
|
||||
config.only_detect_misalignment_via_page_table_on_page_boundary = true;
|
||||
@@ -188,6 +188,13 @@ void ArmDynarmic32::MakeJit(Common::PageTable* page_table) {
|
||||
|
||||
config.fastmem_exclusive_access = config.fastmem_pointer != std::nullopt;
|
||||
config.recompile_on_exclusive_fastmem_failure = true;
|
||||
|
||||
if (reinterpret_cast<u64>(m_system.DeviceMemory().buffer.BackingBasePointer() +
|
||||
Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetIntendedMemorySize()) < (1ULL << 39)) {
|
||||
// Systems like FreeBSD allocate memory really low by default, and since we pack our page table entries,
|
||||
// we have to manually sign extend when our actual pointer is negative.
|
||||
config.page_table_sign_extension = Common::PageTable::SIGN_BIT;
|
||||
}
|
||||
}
|
||||
|
||||
// Multi-process state
|
||||
@@ -420,6 +427,7 @@ void ArmDynarmic32::SignalInterrupt(Kernel::KThread* thread) {
|
||||
}
|
||||
|
||||
void ArmDynarmic32::ClearInstructionCache() {
|
||||
m_cb->last_code_addr = u64(-1);
|
||||
m_jit->ClearCache();
|
||||
}
|
||||
|
||||
|
||||
@@ -211,13 +211,12 @@ void ArmDynarmic64::MakeJit(Common::PageTable* page_table, std::size_t address_s
|
||||
|
||||
// Memory
|
||||
if (page_table) {
|
||||
constexpr size_t PageLog2Stride = 5;
|
||||
static_assert(1 << PageLog2Stride == sizeof(Common::PageTable::PageEntryData));
|
||||
|
||||
config.page_table = reinterpret_cast<void**>(page_table->entries.data());
|
||||
// Dynarmic will not write to the page table, const_cast is safe here
|
||||
config.page_table = reinterpret_cast<void**>(
|
||||
const_cast<Common::PageTable::PageEntryData*>(page_table->entries.data()));
|
||||
config.page_table_address_space_bits = std::uint32_t(address_space_bits);
|
||||
config.page_table_pointer_mask_bits = Common::PageTable::ATTRIBUTE_BITS;
|
||||
config.page_table_log2_stride = PageLog2Stride;
|
||||
config.page_table_pointer_mask = Common::PageTable::ATTRIBUTE_MASK;
|
||||
config.page_table_marked_bit = 0;
|
||||
config.silently_mirror_page_table = false;
|
||||
config.absolute_offset_page_table = true;
|
||||
config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128;
|
||||
@@ -231,6 +230,13 @@ void ArmDynarmic64::MakeJit(Common::PageTable* page_table, std::size_t address_s
|
||||
|
||||
config.fastmem_exclusive_access = config.fastmem_pointer != std::nullopt;
|
||||
config.recompile_on_exclusive_fastmem_failure = true;
|
||||
|
||||
if (reinterpret_cast<u64>(m_system.DeviceMemory().buffer.BackingBasePointer() +
|
||||
Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetIntendedMemorySize()) < (1ULL << 39)) {
|
||||
// Systems like FreeBSD allocate memory really low by default, and since we pack our page table entries,
|
||||
// we have to manually sign extend when our actual pointer is negative.
|
||||
config.page_table_sign_extension = Common::PageTable::SIGN_BIT;
|
||||
}
|
||||
}
|
||||
|
||||
// Multi-process state
|
||||
@@ -447,6 +453,7 @@ void ArmDynarmic64::SignalInterrupt(Kernel::KThread* thread) {
|
||||
}
|
||||
|
||||
void ArmDynarmic64::ClearInstructionCache() {
|
||||
m_cb->last_code_addr = u64(-1);
|
||||
m_jit->ClearCache();
|
||||
}
|
||||
|
||||
|
||||
@@ -121,7 +121,10 @@ union Exclusive {
|
||||
constexpr explicit Exclusive(u32 raw_) : raw{raw_} {}
|
||||
|
||||
constexpr bool Verify() {
|
||||
return this->GetSig() == 0x10;
|
||||
if (this->GetSig() != 0x10) return false;
|
||||
const bool pair = decltype(l)::ExtractValue(raw) & 1;
|
||||
const bool fixed_rt2 = decltype(rt2)::ExtractValue(raw) == 0b11111;
|
||||
return pair || fixed_rt2;
|
||||
}
|
||||
|
||||
constexpr u32 GetSig() {
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// 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
|
||||
|
||||
@@ -26,8 +29,11 @@ public:
|
||||
|
||||
template <typename T>
|
||||
Common::PhysicalAddress GetPhysicalAddr(const T* ptr) const {
|
||||
return (reinterpret_cast<uintptr_t>(ptr) -
|
||||
reinterpret_cast<uintptr_t>(buffer.BackingBasePointer())) +
|
||||
return GetPhysicalAddr(reinterpret_cast<uintptr_t>(ptr));
|
||||
}
|
||||
|
||||
Common::PhysicalAddress GetPhysicalAddr(uintptr_t ptr) const {
|
||||
return (ptr - reinterpret_cast<uintptr_t>(buffer.BackingBasePointer())) +
|
||||
DramMemoryMap::Base;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
#include "common/common_types.h"
|
||||
#include "common/range_mutex.h"
|
||||
#include "common/scratch_buffer.h"
|
||||
#include "common/virtual_buffer.h"
|
||||
#include "common/sparse_large_vector.h"
|
||||
|
||||
namespace Core {
|
||||
|
||||
@@ -178,8 +178,8 @@ private:
|
||||
u32 continuity_tracker;
|
||||
u32 compressed_physical_ptr;
|
||||
};
|
||||
Common::VirtualBuffer<u32> compressed_device_addr;
|
||||
Common::VirtualBuffer<TrackedEntry> tracked_entries;
|
||||
Common::SparseLargeVector<u32> compressed_device_addr;
|
||||
Common::SparseLargeVector<TrackedEntry> tracked_entries;
|
||||
|
||||
// Process memory interfaces
|
||||
|
||||
@@ -200,8 +200,8 @@ private:
|
||||
return std::make_pair(asid, address);
|
||||
}
|
||||
|
||||
void InsertCPUBacking(size_t page_index, VAddr address, Asid asid) {
|
||||
tracked_entries[page_index].cpu_backing_address = address | (asid.id << asid_start_bit);
|
||||
constexpr void InsertCPUBacking(size_t page_index, VAddr address, Asid asid) {
|
||||
tracked_entries.GetUnchecked(page_index).cpu_backing_address = address | (asid.id << asid_start_bit);
|
||||
}
|
||||
|
||||
std::array<TranslationEntry, 4> t_slot{};
|
||||
|
||||
@@ -177,17 +177,6 @@ DeviceMemoryManager<Traits>::DeviceMemoryManager(const DeviceMemory& device_memo
|
||||
{
|
||||
impl = std::make_unique<DeviceMemoryManagerAllocator<Traits>>();
|
||||
cached_pages = std::make_unique<CachedPages>();
|
||||
|
||||
const size_t total_virtual = device_as_size >> Memory::YUZU_PAGEBITS;
|
||||
for (size_t i = 0; i < total_virtual; i++) {
|
||||
tracked_entries[i].compressed_physical_ptr = 0;
|
||||
tracked_entries[i].continuity_tracker = 1;
|
||||
tracked_entries[i].cpu_backing_address = 0;
|
||||
}
|
||||
const size_t total_phys = 1ULL << ((Settings::values.memory_layout_mode.GetValue() == Settings::MemoryLayout::Memory_4Gb ? physical_min_bits : physical_max_bits) - Memory::YUZU_PAGEBITS);
|
||||
for (size_t i = 0; i < total_phys; i++) {
|
||||
compressed_device_addr[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Traits>
|
||||
@@ -220,26 +209,28 @@ void DeviceMemoryManager<Traits>::Map(DAddr address, VAddr virtual_address, size
|
||||
size_t start_page_d = address >> Memory::YUZU_PAGEBITS;
|
||||
size_t num_pages = Common::AlignUp(size, Memory::YUZU_PAGESIZE) >> Memory::YUZU_PAGEBITS;
|
||||
std::scoped_lock lk(mapping_guard);
|
||||
|
||||
tracked_entries.CommitRegion(start_page_d, start_page_d + num_pages);
|
||||
for (size_t i = 0; i < num_pages; i++) {
|
||||
const VAddr new_vaddress = virtual_address + i * Memory::YUZU_PAGESIZE;
|
||||
auto* ptr = process_memory->GetPointerSilent(Common::ProcessAddress(new_vaddress));
|
||||
if (ptr == nullptr) [[unlikely]] {
|
||||
tracked_entries[start_page_d + i].compressed_physical_ptr = 0;
|
||||
tracked_entries.GetUnchecked(start_page_d + i).compressed_physical_ptr = 0;
|
||||
continue;
|
||||
}
|
||||
auto phys_addr = static_cast<u32>(GetRawPhysicalAddr(ptr) >> Memory::YUZU_PAGEBITS) + 1U;
|
||||
tracked_entries[start_page_d + i].compressed_physical_ptr = phys_addr;
|
||||
tracked_entries.GetUnchecked(start_page_d + i).compressed_physical_ptr = phys_addr;
|
||||
InsertCPUBacking(start_page_d + i, new_vaddress, asid);
|
||||
const u32 base_dev = compressed_device_addr[phys_addr - 1U];
|
||||
const u32 new_dev = static_cast<u32>(start_page_d + i);
|
||||
if (base_dev == 0) [[likely]] {
|
||||
compressed_device_addr[phys_addr - 1U] = new_dev;
|
||||
compressed_device_addr.GetAndFault(phys_addr - 1U) = new_dev;
|
||||
continue;
|
||||
}
|
||||
u32 start_id = base_dev & MULTI_MASK;
|
||||
if ((base_dev >> MULTI_FLAG_BITS) == 0) {
|
||||
start_id = impl->multi_dev_address.Register(base_dev);
|
||||
compressed_device_addr[phys_addr - 1U] = MULTI_FLAG | start_id;
|
||||
compressed_device_addr.GetAndFault(phys_addr - 1U) = MULTI_FLAG | start_id;
|
||||
}
|
||||
impl->multi_dev_address.Register(new_dev, start_id);
|
||||
}
|
||||
@@ -255,24 +246,26 @@ void DeviceMemoryManager<Traits>::Unmap(DAddr address, size_t size) {
|
||||
size_t num_pages = Common::AlignUp(size, Memory::YUZU_PAGESIZE) >> Memory::YUZU_PAGEBITS;
|
||||
device_inter->InvalidateRegion(address, size);
|
||||
std::scoped_lock lk(mapping_guard);
|
||||
|
||||
tracked_entries.CommitRegion(start_page_d, start_page_d + num_pages); // should already be committed, but just in case
|
||||
for (size_t i = 0; i < num_pages; i++) {
|
||||
auto phys_addr = tracked_entries[start_page_d + i].compressed_physical_ptr;
|
||||
tracked_entries[start_page_d + i].compressed_physical_ptr = 0;
|
||||
tracked_entries[start_page_d + i].cpu_backing_address = 0;
|
||||
auto& entry = tracked_entries.GetUnchecked(start_page_d + i);
|
||||
auto phys_addr = entry.compressed_physical_ptr;
|
||||
entry.compressed_physical_ptr = 0;
|
||||
entry.cpu_backing_address = 0;
|
||||
if (phys_addr != 0) [[likely]] {
|
||||
const u32 base_dev = compressed_device_addr[phys_addr - 1U];
|
||||
u32& base_dev = compressed_device_addr.GetAndFault(phys_addr - 1U);
|
||||
if ((base_dev >> MULTI_FLAG_BITS) == 0) [[likely]] {
|
||||
compressed_device_addr[phys_addr - 1] = 0;
|
||||
base_dev = 0;
|
||||
continue;
|
||||
}
|
||||
const auto [more_entries, new_start] = impl->multi_dev_address.Unregister(
|
||||
static_cast<u32>(start_page_d + i), base_dev & MULTI_MASK);
|
||||
if (!more_entries) {
|
||||
compressed_device_addr[phys_addr - 1] =
|
||||
impl->multi_dev_address.ReleaseEntry(new_start);
|
||||
base_dev = impl->multi_dev_address.ReleaseEntry(new_start);
|
||||
continue;
|
||||
}
|
||||
compressed_device_addr[phys_addr - 1] = new_start | MULTI_FLAG;
|
||||
base_dev = new_start | MULTI_FLAG;
|
||||
}
|
||||
}
|
||||
t_slot = {};
|
||||
@@ -285,6 +278,8 @@ void DeviceMemoryManager<Traits>::TrackContinuityImpl(DAddr address, VAddr virtu
|
||||
size_t num_pages = Common::AlignUp(size, Memory::YUZU_PAGESIZE) >> Memory::YUZU_PAGEBITS;
|
||||
uintptr_t last_ptr = 0;
|
||||
size_t page_count = 1;
|
||||
|
||||
tracked_entries.CommitRegion(start_page_d, start_page_d + num_pages);
|
||||
for (size_t i = num_pages; i > 0; i--) {
|
||||
size_t index = i - 1;
|
||||
const VAddr new_vaddress = virtual_address + index * Memory::YUZU_PAGESIZE;
|
||||
@@ -296,14 +291,14 @@ void DeviceMemoryManager<Traits>::TrackContinuityImpl(DAddr address, VAddr virtu
|
||||
page_count = 1;
|
||||
}
|
||||
last_ptr = new_ptr;
|
||||
tracked_entries[start_page_d + index].continuity_tracker = static_cast<u32>(page_count);
|
||||
tracked_entries.GetUnchecked(start_page_d + index).continuity_tracker = static_cast<u32>(page_count) - 1;
|
||||
}
|
||||
}
|
||||
template <typename Traits>
|
||||
u8* DeviceMemoryManager<Traits>::GetSpan(const DAddr src_addr, const std::size_t size) {
|
||||
size_t page_index = src_addr >> page_bits;
|
||||
size_t subbits = src_addr & page_mask;
|
||||
if ((static_cast<size_t>(tracked_entries[page_index].continuity_tracker) << page_bits) >= size + subbits) {
|
||||
if ((static_cast<size_t>(tracked_entries[page_index].continuity_tracker+1) << page_bits) >= size + subbits) {
|
||||
return GetPointer<u8>(src_addr);
|
||||
}
|
||||
return nullptr;
|
||||
@@ -313,7 +308,7 @@ template <typename Traits>
|
||||
const u8* DeviceMemoryManager<Traits>::GetSpan(const DAddr src_addr, const std::size_t size) const {
|
||||
size_t page_index = src_addr >> page_bits;
|
||||
size_t subbits = src_addr & page_mask;
|
||||
if ((static_cast<size_t>(tracked_entries[page_index].continuity_tracker) << page_bits) >= size + subbits) {
|
||||
if ((static_cast<size_t>(tracked_entries[page_index].continuity_tracker+1) << page_bits) >= size + subbits) {
|
||||
return GetPointer<u8>(src_addr);
|
||||
}
|
||||
return nullptr;
|
||||
@@ -383,7 +378,7 @@ void DeviceMemoryManager<Traits>::WalkBlock(DAddr addr, std::size_t size, auto o
|
||||
std::size_t page_index = addr >> Memory::YUZU_PAGEBITS;
|
||||
std::size_t page_offset = addr & Memory::YUZU_PAGEMASK;
|
||||
while (remaining_size) {
|
||||
const size_t next_pages = std::size_t(tracked_entries[page_index].continuity_tracker);
|
||||
const size_t next_pages = std::size_t(tracked_entries[page_index].continuity_tracker+1);
|
||||
const std::size_t copy_amount = (std::min)((next_pages << Memory::YUZU_PAGEBITS) - page_offset, remaining_size);
|
||||
const auto current_vaddr = u64((page_index << Memory::YUZU_PAGEBITS) + page_offset);
|
||||
SCOPE_EXIT{
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// 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
|
||||
|
||||
@@ -121,6 +124,7 @@ size_t HierarchicalIntegrityVerificationStorage::Read(u8* buffer, size_t size,
|
||||
}
|
||||
|
||||
size_t HierarchicalIntegrityVerificationStorage::GetSize() const {
|
||||
ASSERT(m_data_size >= 0);
|
||||
return m_data_size;
|
||||
}
|
||||
|
||||
|
||||
@@ -120,7 +120,7 @@ std::array<u8, 32> IPSwitchCompiler::GetBuildID() const {
|
||||
|
||||
static IPSwitchRecord EscapeStringSequences(std::string_view sv) {
|
||||
IPSwitchRecord r{};
|
||||
for (auto it = sv.cbegin(); it != sv.cend(); ) {
|
||||
for (auto it = sv.cbegin(); it < sv.cend(); ) {
|
||||
if (*it == '\\' && it + 1 < sv.cend()) {
|
||||
switch (it[1]) {
|
||||
case 'a': r.data[r.count] = '\a'; break;
|
||||
@@ -198,6 +198,8 @@ void IPSwitchCompiler::Parse(std::span<u8 const> bytes) {
|
||||
LOG_WARNING(Loader, "Unknown flag {}", line);
|
||||
break;
|
||||
}
|
||||
} else if (patches.empty()) {
|
||||
LOG_WARNING(Loader, "Invalid line not in a patch {}", line);
|
||||
} else {
|
||||
size_t offset = size_t(std::strtoul(line.data(), nullptr, 16));
|
||||
offset += size_t(offset_shift);
|
||||
@@ -253,25 +255,30 @@ void IPSwitchCompiler::Parse(std::span<u8 const> bytes) {
|
||||
// now make a nominal preprocessed line: remove comments
|
||||
char quote = '\0';
|
||||
auto const sline_start = p;
|
||||
auto last_char = p;
|
||||
for (; p < sline.cend(); ) {
|
||||
// we dont check for "//", IPS checks for '/' only...
|
||||
if ((!quote && p[0] == '/')
|
||||
if (std::isspace(*p)) {
|
||||
++p;
|
||||
} else if ((!quote && p[0] == '/')
|
||||
|| (!quote && p[0] == '#')) {
|
||||
break;
|
||||
} else if (p[0] == '\"' || p[0] == '\'') {
|
||||
quote = (p[0] == quote) ? '\0' : p[0];
|
||||
++p;
|
||||
last_char = p;
|
||||
} else if (p + 1 < sline.cend() && p[0] == '\\') {
|
||||
p += 2;
|
||||
last_char = p;
|
||||
} else {
|
||||
++p;
|
||||
last_char = p;
|
||||
}
|
||||
}
|
||||
// now we have the preprocessed string ;)
|
||||
std::string_view pp_str(sline_start, p);
|
||||
if (pp_str.size() > 0 && !parse_line(pp_str)) {
|
||||
std::string_view const pp_str(sline_start, last_char);
|
||||
if (pp_str.size() > 0 && !parse_line(pp_str))
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,8 +86,7 @@ constexpr size_t SlabCountKDeviceAddressSpace = 300;
|
||||
constexpr size_t SlabCountKSession = 1133;
|
||||
constexpr size_t SlabCountKLightSession = 100;
|
||||
constexpr size_t SlabCountKObjectName = 7;
|
||||
// TODO(lizzie): divergence that allows ulauncher to work
|
||||
constexpr size_t SlabCountKResourceLimit = 5 + 1;
|
||||
constexpr size_t SlabCountKResourceLimit = 5;
|
||||
constexpr size_t SlabCountKDebug = Core::Hardware::NUM_CPU_CORES;
|
||||
constexpr size_t SlabCountKIoPool = 1;
|
||||
constexpr size_t SlabCountKIoRegion = 6;
|
||||
|
||||
@@ -635,6 +635,36 @@ Result KPageTableBase::CheckMemoryState(const KMemoryInfo& info, KMemoryState st
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
bool KPageTableBase::BeginTraversal(const Common::PageTable &impl, TraversalEntry *out_entry, TraversalContext *out_context,
|
||||
Common::ProcessAddress address) const {
|
||||
out_context->next_offset = GetInteger(address);
|
||||
out_context->next_page = GetInteger(address) >> PageBits;
|
||||
|
||||
return ContinueTraversal(impl, out_entry, out_context);
|
||||
}
|
||||
|
||||
bool KPageTableBase::ContinueTraversal(const Common::PageTable &impl, TraversalEntry *out_entry,
|
||||
TraversalContext *context) const {
|
||||
// Setup invalid defaults.
|
||||
out_entry->phys_addr = 0;
|
||||
out_entry->block_size = PageSize;
|
||||
// Validate that we can read the actual entry.
|
||||
if (auto const page = context->next_page; page < impl.entries.size()) {
|
||||
// Validate that the entry is mapped.
|
||||
if (auto const paddr = impl.entries[page].Pointer(true); paddr != 0) {
|
||||
// Populate the results and return true
|
||||
out_entry->phys_addr = GetInteger(m_system.DeviceMemory().GetPhysicalAddr(paddr + context->next_offset));
|
||||
context->next_page += 1;
|
||||
context->next_offset += PageSize;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
context->next_page += 1;
|
||||
context->next_offset += PageSize;
|
||||
// Otherwise return false
|
||||
return false;
|
||||
}
|
||||
|
||||
Result KPageTableBase::CheckMemoryStateContiguous(size_t* out_blocks_needed, KProcessAddress addr,
|
||||
size_t size, KMemoryState state_mask,
|
||||
KMemoryState state, KMemoryPermission perm_mask,
|
||||
@@ -940,7 +970,7 @@ Result KPageTableBase::QueryMappingImpl(KProcessAddress* out, KPhysicalAddress a
|
||||
size_t tot_size = 0;
|
||||
|
||||
next_valid =
|
||||
impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), region_start);
|
||||
BeginTraversal(impl, std::addressof(next_entry), std::addressof(context), region_start);
|
||||
next_entry.block_size =
|
||||
(next_entry.block_size - (GetInteger(region_start) & (next_entry.block_size - 1)));
|
||||
|
||||
@@ -976,7 +1006,7 @@ Result KPageTableBase::QueryMappingImpl(KProcessAddress* out, KPhysicalAddress a
|
||||
break;
|
||||
}
|
||||
|
||||
next_valid = impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
|
||||
next_valid = ContinueTraversal(impl, std::addressof(next_entry), std::addressof(context));
|
||||
}
|
||||
|
||||
// Check the last entry.
|
||||
@@ -1754,7 +1784,7 @@ Result KPageTableBase::MakePageGroup(KPageGroup& pg, KProcessAddress addr, size_
|
||||
// Begin traversal.
|
||||
TraversalContext context;
|
||||
TraversalEntry next_entry;
|
||||
R_UNLESS(impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), addr),
|
||||
R_UNLESS(BeginTraversal(impl, std::addressof(next_entry), std::addressof(context), addr),
|
||||
ResultInvalidCurrentMemory);
|
||||
|
||||
// Prepare tracking variables.
|
||||
@@ -1764,7 +1794,7 @@ Result KPageTableBase::MakePageGroup(KPageGroup& pg, KProcessAddress addr, size_
|
||||
|
||||
// Iterate, adding to group as we go.
|
||||
while (tot_size < size) {
|
||||
R_UNLESS(impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context)),
|
||||
R_UNLESS(ContinueTraversal(impl, std::addressof(next_entry), std::addressof(context)),
|
||||
ResultInvalidCurrentMemory);
|
||||
|
||||
if (next_entry.phys_addr != (cur_addr + cur_size)) {
|
||||
@@ -1828,7 +1858,7 @@ bool KPageTableBase::IsValidPageGroup(const KPageGroup& pg, KProcessAddress addr
|
||||
// Begin traversal.
|
||||
TraversalContext context;
|
||||
TraversalEntry next_entry;
|
||||
if (!impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), addr)) {
|
||||
if (!BeginTraversal(impl, std::addressof(next_entry), std::addressof(context), addr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1839,7 +1869,7 @@ bool KPageTableBase::IsValidPageGroup(const KPageGroup& pg, KProcessAddress addr
|
||||
|
||||
// Iterate, comparing expected to actual.
|
||||
while (tot_size < size) {
|
||||
if (!impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context))) {
|
||||
if (!ContinueTraversal(impl, std::addressof(next_entry), std::addressof(context))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1896,7 +1926,7 @@ Result KPageTableBase::GetContiguousMemoryRangeWithState(
|
||||
// Begin a traversal.
|
||||
TraversalContext context;
|
||||
TraversalEntry cur_entry = {.phys_addr = 0, .block_size = 0};
|
||||
R_UNLESS(impl.BeginTraversal(std::addressof(cur_entry), std::addressof(context), address),
|
||||
R_UNLESS(BeginTraversal(impl, std::addressof(cur_entry), std::addressof(context), address),
|
||||
ResultInvalidCurrentMemory);
|
||||
|
||||
// Traverse until we have enough size or we aren't contiguous any more.
|
||||
@@ -1905,7 +1935,7 @@ Result KPageTableBase::GetContiguousMemoryRangeWithState(
|
||||
for (contig_size =
|
||||
cur_entry.block_size - (GetInteger(phys_address) & (cur_entry.block_size - 1));
|
||||
contig_size < size; contig_size += cur_entry.block_size) {
|
||||
if (!impl.ContinueTraversal(std::addressof(cur_entry), std::addressof(context))) {
|
||||
if (!ContinueTraversal(impl, std::addressof(cur_entry), std::addressof(context))) {
|
||||
break;
|
||||
}
|
||||
if (cur_entry.phys_addr != phys_address + contig_size) {
|
||||
@@ -2334,7 +2364,7 @@ Result KPageTableBase::QueryPhysicalAddress(Svc::lp64::PhysicalMemoryInfo* out,
|
||||
TraversalContext context;
|
||||
TraversalEntry next_entry;
|
||||
bool traverse_valid =
|
||||
m_impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), virt_addr);
|
||||
BeginTraversal(m_impl, std::addressof(next_entry), std::addressof(context), virt_addr);
|
||||
R_UNLESS(traverse_valid, ResultInvalidCurrentMemory);
|
||||
|
||||
// Set tracking variables.
|
||||
@@ -2345,7 +2375,7 @@ Result KPageTableBase::QueryPhysicalAddress(Svc::lp64::PhysicalMemoryInfo* out,
|
||||
while (true) {
|
||||
// Continue the traversal.
|
||||
traverse_valid =
|
||||
m_impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
|
||||
ContinueTraversal(m_impl, std::addressof(next_entry), std::addressof(context));
|
||||
if (!traverse_valid) {
|
||||
break;
|
||||
}
|
||||
@@ -2567,7 +2597,7 @@ Result KPageTableBase::UnmapIoRegion(KProcessAddress dst_address, KPhysicalAddre
|
||||
TraversalContext context;
|
||||
TraversalEntry next_entry;
|
||||
ASSERT(
|
||||
impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), dst_address));
|
||||
BeginTraversal(impl, std::addressof(next_entry), std::addressof(context), dst_address));
|
||||
|
||||
// Check that the physical region matches.
|
||||
R_UNLESS(next_entry.phys_addr == phys_addr, ResultInvalidMemoryRegion);
|
||||
@@ -2577,7 +2607,7 @@ Result KPageTableBase::UnmapIoRegion(KProcessAddress dst_address, KPhysicalAddre
|
||||
next_entry.block_size - (GetInteger(phys_addr) & (next_entry.block_size - 1));
|
||||
checked_size < size; checked_size += next_entry.block_size) {
|
||||
// Continue the traversal.
|
||||
ASSERT(impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context)));
|
||||
ASSERT(ContinueTraversal(impl, std::addressof(next_entry), std::addressof(context)));
|
||||
|
||||
// Check that the physical region matches.
|
||||
R_UNLESS(next_entry.phys_addr == phys_addr + checked_size, ResultInvalidMemoryRegion);
|
||||
@@ -3029,7 +3059,7 @@ Result KPageTableBase::InvalidateProcessDataCache(KProcessAddress address, size_
|
||||
TraversalContext context;
|
||||
TraversalEntry next_entry;
|
||||
bool traverse_valid =
|
||||
impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), address);
|
||||
BeginTraversal(impl, std::addressof(next_entry), std::addressof(context), address);
|
||||
R_UNLESS(traverse_valid, ResultInvalidCurrentMemory);
|
||||
|
||||
// Prepare tracking variables.
|
||||
@@ -3041,7 +3071,7 @@ Result KPageTableBase::InvalidateProcessDataCache(KProcessAddress address, size_
|
||||
while (tot_size < size) {
|
||||
// Continue the traversal.
|
||||
traverse_valid =
|
||||
impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
|
||||
ContinueTraversal(impl, std::addressof(next_entry), std::addressof(context));
|
||||
R_UNLESS(traverse_valid, ResultInvalidCurrentMemory);
|
||||
|
||||
if (next_entry.phys_addr != (cur_addr + cur_size)) {
|
||||
@@ -3129,7 +3159,7 @@ Result KPageTableBase::ReadDebugMemory(KProcessAddress dst_address, KProcessAddr
|
||||
TraversalContext context;
|
||||
TraversalEntry next_entry;
|
||||
bool traverse_valid =
|
||||
impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), src_address);
|
||||
BeginTraversal(impl, std::addressof(next_entry), std::addressof(context), src_address);
|
||||
R_UNLESS(traverse_valid, ResultInvalidCurrentMemory);
|
||||
|
||||
// Prepare tracking variables.
|
||||
@@ -3167,7 +3197,7 @@ Result KPageTableBase::ReadDebugMemory(KProcessAddress dst_address, KProcessAddr
|
||||
while (tot_size < size) {
|
||||
// Continue the traversal.
|
||||
traverse_valid =
|
||||
impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
|
||||
ContinueTraversal(impl, std::addressof(next_entry), std::addressof(context));
|
||||
ASSERT(traverse_valid);
|
||||
|
||||
if (next_entry.phys_addr != (cur_addr + cur_size)) {
|
||||
@@ -3225,7 +3255,7 @@ Result KPageTableBase::WriteDebugMemory(KProcessAddress dst_address, KProcessAdd
|
||||
TraversalContext context;
|
||||
TraversalEntry next_entry;
|
||||
bool traverse_valid =
|
||||
impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), dst_address);
|
||||
BeginTraversal(impl, std::addressof(next_entry), std::addressof(context), dst_address);
|
||||
R_UNLESS(traverse_valid, ResultInvalidCurrentMemory);
|
||||
|
||||
// Prepare tracking variables.
|
||||
@@ -3267,7 +3297,7 @@ Result KPageTableBase::WriteDebugMemory(KProcessAddress dst_address, KProcessAdd
|
||||
while (tot_size < size) {
|
||||
// Continue the traversal.
|
||||
traverse_valid =
|
||||
impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
|
||||
ContinueTraversal(impl, std::addressof(next_entry), std::addressof(context));
|
||||
ASSERT(traverse_valid);
|
||||
|
||||
if (next_entry.phys_addr != (cur_addr + cur_size)) {
|
||||
@@ -3728,7 +3758,7 @@ Result KPageTableBase::CopyMemoryFromLinearToUser(
|
||||
TraversalContext context;
|
||||
TraversalEntry next_entry;
|
||||
bool traverse_valid =
|
||||
impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), src_addr);
|
||||
BeginTraversal(impl, std::addressof(next_entry), std::addressof(context), src_addr);
|
||||
ASSERT(traverse_valid);
|
||||
|
||||
// Prepare tracking variables.
|
||||
@@ -3768,7 +3798,7 @@ Result KPageTableBase::CopyMemoryFromLinearToUser(
|
||||
while (tot_size < size) {
|
||||
// Continue the traversal.
|
||||
traverse_valid =
|
||||
impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
|
||||
ContinueTraversal(impl, std::addressof(next_entry), std::addressof(context));
|
||||
ASSERT(traverse_valid);
|
||||
|
||||
if (next_entry.phys_addr != (cur_addr + cur_size)) {
|
||||
@@ -3822,7 +3852,7 @@ Result KPageTableBase::CopyMemoryFromLinearToKernel(
|
||||
TraversalContext context;
|
||||
TraversalEntry next_entry;
|
||||
bool traverse_valid =
|
||||
impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), src_addr);
|
||||
BeginTraversal(impl, std::addressof(next_entry), std::addressof(context), src_addr);
|
||||
ASSERT(traverse_valid);
|
||||
|
||||
// Prepare tracking variables.
|
||||
@@ -3845,7 +3875,7 @@ Result KPageTableBase::CopyMemoryFromLinearToKernel(
|
||||
while (tot_size < size) {
|
||||
// Continue the traversal.
|
||||
traverse_valid =
|
||||
impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
|
||||
ContinueTraversal(impl, std::addressof(next_entry), std::addressof(context));
|
||||
ASSERT(traverse_valid);
|
||||
|
||||
if (next_entry.phys_addr != (cur_addr + cur_size)) {
|
||||
@@ -3902,7 +3932,7 @@ Result KPageTableBase::CopyMemoryFromUserToLinear(
|
||||
TraversalContext context;
|
||||
TraversalEntry next_entry;
|
||||
bool traverse_valid =
|
||||
impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), dst_addr);
|
||||
BeginTraversal(impl, std::addressof(next_entry), std::addressof(context), dst_addr);
|
||||
ASSERT(traverse_valid);
|
||||
|
||||
// Prepare tracking variables.
|
||||
@@ -3941,7 +3971,7 @@ Result KPageTableBase::CopyMemoryFromUserToLinear(
|
||||
while (tot_size < size) {
|
||||
// Continue the traversal.
|
||||
traverse_valid =
|
||||
impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
|
||||
ContinueTraversal(impl, std::addressof(next_entry), std::addressof(context));
|
||||
ASSERT(traverse_valid);
|
||||
|
||||
if (next_entry.phys_addr != (cur_addr + cur_size)) {
|
||||
@@ -3997,7 +4027,7 @@ Result KPageTableBase::CopyMemoryFromKernelToLinear(KProcessAddress dst_addr, si
|
||||
TraversalContext context;
|
||||
TraversalEntry next_entry;
|
||||
bool traverse_valid =
|
||||
impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), dst_addr);
|
||||
BeginTraversal(impl, std::addressof(next_entry), std::addressof(context), dst_addr);
|
||||
ASSERT(traverse_valid);
|
||||
|
||||
// Prepare tracking variables.
|
||||
@@ -4020,7 +4050,7 @@ Result KPageTableBase::CopyMemoryFromKernelToLinear(KProcessAddress dst_addr, si
|
||||
while (tot_size < size) {
|
||||
// Continue the traversal.
|
||||
traverse_valid =
|
||||
impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
|
||||
ContinueTraversal(impl, std::addressof(next_entry), std::addressof(context));
|
||||
ASSERT(traverse_valid);
|
||||
|
||||
if (next_entry.phys_addr != (cur_addr + cur_size)) {
|
||||
@@ -4089,10 +4119,10 @@ Result KPageTableBase::CopyMemoryFromHeapToHeap(
|
||||
bool traverse_valid;
|
||||
|
||||
// Begin traversal.
|
||||
traverse_valid = src_impl.BeginTraversal(std::addressof(src_next_entry),
|
||||
traverse_valid = BeginTraversal(src_impl, std::addressof(src_next_entry),
|
||||
std::addressof(src_context), src_addr);
|
||||
ASSERT(traverse_valid);
|
||||
traverse_valid = dst_impl.BeginTraversal(std::addressof(dst_next_entry),
|
||||
traverse_valid = BeginTraversal(dst_impl, std::addressof(dst_next_entry),
|
||||
std::addressof(dst_context), dst_addr);
|
||||
ASSERT(traverse_valid);
|
||||
|
||||
@@ -4127,7 +4157,7 @@ Result KPageTableBase::CopyMemoryFromHeapToHeap(
|
||||
if (ofs + cur_copy_size != size) {
|
||||
if (cur_src_addr + cur_min_size == cur_src_block_addr + cur_src_size) {
|
||||
// Continue the src traversal.
|
||||
traverse_valid = src_impl.ContinueTraversal(std::addressof(src_next_entry),
|
||||
traverse_valid = ContinueTraversal(src_impl, std::addressof(src_next_entry),
|
||||
std::addressof(src_context));
|
||||
ASSERT(traverse_valid);
|
||||
|
||||
@@ -4138,7 +4168,7 @@ Result KPageTableBase::CopyMemoryFromHeapToHeap(
|
||||
if (cur_dst_addr + cur_min_size ==
|
||||
dst_next_entry.phys_addr + dst_next_entry.block_size) {
|
||||
// Continue the dst traversal.
|
||||
traverse_valid = dst_impl.ContinueTraversal(std::addressof(dst_next_entry),
|
||||
traverse_valid = ContinueTraversal(dst_impl, std::addressof(dst_next_entry),
|
||||
std::addressof(dst_context));
|
||||
ASSERT(traverse_valid);
|
||||
|
||||
@@ -4223,10 +4253,10 @@ Result KPageTableBase::CopyMemoryFromHeapToHeapWithoutCheckDestination(
|
||||
bool traverse_valid;
|
||||
|
||||
// Begin traversal.
|
||||
traverse_valid = src_impl.BeginTraversal(std::addressof(src_next_entry),
|
||||
traverse_valid = BeginTraversal(src_impl, std::addressof(src_next_entry),
|
||||
std::addressof(src_context), src_addr);
|
||||
ASSERT(traverse_valid);
|
||||
traverse_valid = dst_impl.BeginTraversal(std::addressof(dst_next_entry),
|
||||
traverse_valid = BeginTraversal(dst_impl, std::addressof(dst_next_entry),
|
||||
std::addressof(dst_context), dst_addr);
|
||||
ASSERT(traverse_valid);
|
||||
|
||||
@@ -4261,7 +4291,7 @@ Result KPageTableBase::CopyMemoryFromHeapToHeapWithoutCheckDestination(
|
||||
if (ofs + cur_copy_size != size) {
|
||||
if (cur_src_addr + cur_min_size == cur_src_block_addr + cur_src_size) {
|
||||
// Continue the src traversal.
|
||||
traverse_valid = src_impl.ContinueTraversal(std::addressof(src_next_entry),
|
||||
traverse_valid = ContinueTraversal(src_impl, std::addressof(src_next_entry),
|
||||
std::addressof(src_context));
|
||||
ASSERT(traverse_valid);
|
||||
|
||||
@@ -4272,7 +4302,7 @@ Result KPageTableBase::CopyMemoryFromHeapToHeapWithoutCheckDestination(
|
||||
if (cur_dst_addr + cur_min_size ==
|
||||
dst_next_entry.phys_addr + dst_next_entry.block_size) {
|
||||
// Continue the dst traversal.
|
||||
traverse_valid = dst_impl.ContinueTraversal(std::addressof(dst_next_entry),
|
||||
traverse_valid = ContinueTraversal(dst_impl, std::addressof(dst_next_entry),
|
||||
std::addressof(dst_context));
|
||||
ASSERT(traverse_valid);
|
||||
|
||||
@@ -4547,7 +4577,7 @@ Result KPageTableBase::SetupForIpcServer(KProcessAddress* out_addr, size_t size,
|
||||
// Begin traversal.
|
||||
TraversalContext context;
|
||||
TraversalEntry next_entry;
|
||||
bool traverse_valid = src_impl.BeginTraversal(std::addressof(next_entry),
|
||||
bool traverse_valid = BeginTraversal(src_impl, std::addressof(next_entry),
|
||||
std::addressof(context), aligned_src_start);
|
||||
ASSERT(traverse_valid);
|
||||
|
||||
@@ -4597,7 +4627,7 @@ Result KPageTableBase::SetupForIpcServer(KProcessAddress* out_addr, size_t size,
|
||||
// If the block's size was one page, we may need to continue traversal.
|
||||
if (cur_block_size == 0 && aligned_src_size > PageSize) {
|
||||
traverse_valid =
|
||||
src_impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
|
||||
ContinueTraversal(src_impl, std::addressof(next_entry), std::addressof(context));
|
||||
ASSERT(traverse_valid);
|
||||
|
||||
cur_block_addr = next_entry.phys_addr;
|
||||
@@ -4610,7 +4640,7 @@ Result KPageTableBase::SetupForIpcServer(KProcessAddress* out_addr, size_t size,
|
||||
while (aligned_src_start + tot_block_size < mapping_src_end) {
|
||||
// Continue the traversal.
|
||||
traverse_valid =
|
||||
src_impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
|
||||
ContinueTraversal(src_impl, std::addressof(next_entry), std::addressof(context));
|
||||
ASSERT(traverse_valid);
|
||||
|
||||
// Process the block.
|
||||
@@ -4653,7 +4683,7 @@ Result KPageTableBase::SetupForIpcServer(KProcessAddress* out_addr, size_t size,
|
||||
if (mapped_block_end + cur_block_size < aligned_src_end &&
|
||||
cur_block_size == last_block_size) {
|
||||
traverse_valid =
|
||||
src_impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
|
||||
ContinueTraversal(src_impl, std::addressof(next_entry), std::addressof(context));
|
||||
ASSERT(traverse_valid);
|
||||
|
||||
cur_block_addr = next_entry.phys_addr;
|
||||
@@ -5601,7 +5631,7 @@ Result KPageTableBase::UnmapProcessMemory(KProcessAddress dst_address, size_t si
|
||||
ContiguousRangeInfo(KPageTableBase& pt, KProcessAddress address, size_t size)
|
||||
: m_pt(pt), m_remaining_size(size) {
|
||||
// Begin a traversal.
|
||||
ASSERT(m_pt.GetImpl().BeginTraversal(std::addressof(m_entry),
|
||||
ASSERT(m_pt.BeginTraversal(m_pt.GetImpl(), std::addressof(m_entry),
|
||||
std::addressof(m_context), address));
|
||||
|
||||
// Setup tracking fields.
|
||||
@@ -5632,7 +5662,7 @@ Result KPageTableBase::UnmapProcessMemory(KProcessAddress dst_address, size_t si
|
||||
void DetermineContiguousBlockExtents() {
|
||||
// Continue traversing until we're not contiguous, or we have enough.
|
||||
while (m_cur_size < m_remaining_size) {
|
||||
ASSERT(m_pt.GetImpl().ContinueTraversal(std::addressof(m_entry),
|
||||
ASSERT(m_pt.ContinueTraversal(m_pt.GetImpl(), std::addressof(m_entry),
|
||||
std::addressof(m_context)));
|
||||
|
||||
// If we're not contiguous, we're done.
|
||||
|
||||
@@ -370,6 +370,10 @@ private:
|
||||
size_t num_pages, size_t alignment, size_t offset,
|
||||
size_t guard_pages) const;
|
||||
|
||||
bool BeginTraversal(const Common::PageTable& impl, TraversalEntry* out_entry, TraversalContext* out_context,
|
||||
Common::ProcessAddress address) const;
|
||||
bool ContinueTraversal(const Common::PageTable& impl, TraversalEntry* out_entry, TraversalContext* context) const;
|
||||
|
||||
Result CheckMemoryStateContiguous(size_t* out_blocks_needed, KProcessAddress addr, size_t size,
|
||||
KMemoryState state_mask, KMemoryState state,
|
||||
KMemoryPermission perm_mask, KMemoryPermission perm,
|
||||
@@ -474,7 +478,14 @@ private:
|
||||
// Validate pre-conditions.
|
||||
ASSERT(this->IsLockedByCurrentThread());
|
||||
|
||||
return this->GetImpl().GetPhysicalAddress(out, virt_addr);
|
||||
if (virt_addr > (1ULL << m_address_space_width)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*out = m_system.DeviceMemory().GetPhysicalAddr(
|
||||
this->GetImpl().entries[GetInteger(virt_addr) >> PageBits].Pointer(true) + GetInteger(virt_addr));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-late
|
||||
@@ -2501,14 +2501,15 @@ void Call(Core::System& system, u32 imm) {
|
||||
auto& process = GetCurrentProcess(kernel);
|
||||
std::array<uint64_t, 8> args;
|
||||
kernel.CurrentPhysicalCore().SaveSvcArguments(process, args);
|
||||
LOG_TRACE(Kernel_SVC, "{} [0]={:#x} [1]={:#x} [2]={:#x} [3]={:#x} [4]={:#x} [5]={:#x} [6]={:#x}",
|
||||
imm, GetArg32(args, 0), GetArg32(args, 1), GetArg32(args, 2),
|
||||
GetArg32(args, 3), GetArg32(args, 4), GetArg32(args, 5), GetArg32(args, 6));
|
||||
//kernel.EnterSVCProfile();
|
||||
if (process.Is64Bit())
|
||||
LOG_TRACE(Kernel_SVC, "{} [0]={:#x} [1]={:#x} [2]={:#x} [3]={:#x} [4]={:#x} [5]={:#x} [6]={:#x}",
|
||||
imm, GetArg64(args, 0), GetArg64(args, 1), GetArg64(args, 2),
|
||||
GetArg64(args, 3), GetArg64(args, 4), GetArg64(args, 5), GetArg64(args, 6));
|
||||
if (process.Is64Bit()) {
|
||||
Call64(system, imm, args);
|
||||
else
|
||||
} else {
|
||||
Call32(system, imm, args);
|
||||
}
|
||||
//kernel.ExitSVCProfile();
|
||||
kernel.CurrentPhysicalCore().LoadSvcArguments(process, args);
|
||||
}
|
||||
|
||||
@@ -10,17 +10,15 @@
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/kernel/k_resource_limit.h"
|
||||
#include "core/hle/kernel/svc.h"
|
||||
#include "core/hle/kernel/svc_results.h"
|
||||
#include "core/hle/kernel/svc_version.h"
|
||||
|
||||
namespace Kernel::Svc {
|
||||
|
||||
/// Gets system/memory information for the current process
|
||||
Result GetInfo(Core::System& system, u64* result, InfoType info_id_type, Handle handle,
|
||||
u64 info_sub_id) {
|
||||
LOG_TRACE(Kernel_SVC, "called info_id={:#x}, info_sub_id={:#x}, handle={:#08x}",
|
||||
info_id_type, info_sub_id, handle);
|
||||
|
||||
u32 info_id = static_cast<u32>(info_id_type);
|
||||
|
||||
Result GetInfo(Core::System& system, u64* result, InfoType info_id_type, Handle handle, u64 info_sub_id) {
|
||||
LOG_TRACE(Kernel_SVC, "called info_id={:#x}, info_sub_id={:#x}, handle={:#08x}", info_id_type, info_sub_id, handle);
|
||||
u32 info_id = u32(info_id_type);
|
||||
switch (info_id_type) {
|
||||
case InfoType::CoreMask:
|
||||
case InfoType::PriorityMask:
|
||||
@@ -53,152 +51,123 @@ Result GetInfo(Core::System& system, u64* result, InfoType info_id_type, Handle
|
||||
case InfoType::CoreMask:
|
||||
*result = process->GetCoreMask();
|
||||
R_SUCCEED();
|
||||
|
||||
case InfoType::PriorityMask:
|
||||
*result = process->GetPriorityMask();
|
||||
R_SUCCEED();
|
||||
|
||||
case InfoType::AliasRegionAddress:
|
||||
*result = GetInteger(process->GetPageTable().GetAliasRegionStart());
|
||||
R_SUCCEED();
|
||||
|
||||
case InfoType::AliasRegionSize:
|
||||
*result = process->GetPageTable().GetAliasRegionSize();
|
||||
R_SUCCEED();
|
||||
case InfoType::HeapRegionAddress:
|
||||
*result = GetInteger(process->GetPageTable().GetHeapRegionStart());
|
||||
R_SUCCEED();
|
||||
|
||||
case InfoType::HeapRegionSize:
|
||||
*result = process->GetPageTable().GetHeapRegionSize();
|
||||
R_SUCCEED();
|
||||
|
||||
case InfoType::AslrRegionAddress:
|
||||
*result = GetInteger(process->GetPageTable().GetAliasCodeRegionStart());
|
||||
R_SUCCEED();
|
||||
|
||||
case InfoType::AslrRegionSize:
|
||||
*result = process->GetPageTable().GetAliasCodeRegionSize();
|
||||
R_SUCCEED();
|
||||
|
||||
case InfoType::StackRegionAddress:
|
||||
*result = GetInteger(process->GetPageTable().GetStackRegionStart());
|
||||
R_SUCCEED();
|
||||
|
||||
case InfoType::StackRegionSize:
|
||||
*result = process->GetPageTable().GetStackRegionSize();
|
||||
R_SUCCEED();
|
||||
|
||||
case InfoType::TotalMemorySize:
|
||||
*result = process->GetTotalUserPhysicalMemorySize(system.Kernel());
|
||||
R_SUCCEED();
|
||||
|
||||
case InfoType::UsedMemorySize:
|
||||
*result = process->GetUsedUserPhysicalMemorySize(system.Kernel());
|
||||
R_SUCCEED();
|
||||
|
||||
case InfoType::SystemResourceSizeTotal:
|
||||
*result = process->GetTotalSystemResourceSize();
|
||||
R_SUCCEED();
|
||||
|
||||
case InfoType::SystemResourceSizeUsed:
|
||||
*result = process->GetUsedSystemResourceSize();
|
||||
R_SUCCEED();
|
||||
|
||||
case InfoType::ProgramId:
|
||||
*result = process->GetProgramId();
|
||||
R_SUCCEED();
|
||||
|
||||
case InfoType::UserExceptionContextAddress:
|
||||
*result = GetInteger(process->GetProcessLocalRegionAddress());
|
||||
R_SUCCEED();
|
||||
|
||||
case InfoType::TotalNonSystemMemorySize:
|
||||
*result = process->GetTotalNonSystemUserPhysicalMemorySize(system.Kernel());
|
||||
R_SUCCEED();
|
||||
|
||||
case InfoType::UsedNonSystemMemorySize:
|
||||
*result = process->GetUsedNonSystemUserPhysicalMemorySize(system.Kernel());
|
||||
R_SUCCEED();
|
||||
|
||||
case InfoType::IsApplication:
|
||||
*result = process->IsApplication();
|
||||
R_SUCCEED();
|
||||
|
||||
case InfoType::FreeThreadCount:
|
||||
if (KResourceLimit* resource_limit = process->GetResourceLimit();
|
||||
resource_limit != nullptr) {
|
||||
const auto current_value =
|
||||
resource_limit->GetCurrentValue(Svc::LimitableResource::ThreadCountMax);
|
||||
const auto limit_value =
|
||||
resource_limit->GetLimitValue(Svc::LimitableResource::ThreadCountMax);
|
||||
const auto current_value = resource_limit->GetCurrentValue(Svc::LimitableResource::ThreadCountMax);
|
||||
const auto limit_value = resource_limit->GetLimitValue(Svc::LimitableResource::ThreadCountMax);
|
||||
*result = limit_value - current_value;
|
||||
} else {
|
||||
*result = 0;
|
||||
}
|
||||
R_SUCCEED();
|
||||
|
||||
case InfoType::AliasRegionExtraSize: {
|
||||
if (info_sub_id != 0) {
|
||||
return ResultInvalidCombination;
|
||||
}
|
||||
|
||||
R_UNLESS(info_sub_id == 0, ResultInvalidCombination);
|
||||
KProcess* current_process = GetCurrentProcessPointer(system.Kernel());
|
||||
*result = current_process->GetPageTable().GetAliasRegionExtraSize();
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
LOG_ERROR(Kernel_SVC, "Unimplemented svcGetInfo id={:#016x}", info_id);
|
||||
R_THROW(ResultInvalidEnumValue);
|
||||
}
|
||||
|
||||
case InfoType::DebuggerAttached:
|
||||
*result = 0;
|
||||
R_SUCCEED();
|
||||
|
||||
case InfoType::ResourceLimit: {
|
||||
R_UNLESS(handle == 0, ResultInvalidHandle);
|
||||
R_UNLESS(info_sub_id == 0, ResultInvalidCombination);
|
||||
|
||||
KProcess* const current_process = GetCurrentProcessPointer(system.Kernel());
|
||||
KHandleTable& handle_table = current_process->GetHandleTable();
|
||||
const auto resource_limit = current_process->GetResourceLimit();
|
||||
if (!resource_limit) {
|
||||
if (auto const resource_limit = current_process->GetResourceLimit(); resource_limit) {
|
||||
Handle resource_handle{};
|
||||
R_TRY(handle_table.Add(system.Kernel(), std::addressof(resource_handle), resource_limit));
|
||||
*result = resource_handle;
|
||||
} else {
|
||||
*result = Svc::InvalidHandle;
|
||||
// Yes, the kernel considers this a successful operation.
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Handle resource_handle{};
|
||||
R_TRY(handle_table.Add(system.Kernel(), std::addressof(resource_handle), resource_limit));
|
||||
|
||||
*result = resource_handle;
|
||||
// Yes, the kernel considers this a successful operation either way.
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
case InfoType::RandomEntropy:
|
||||
R_UNLESS(handle == 0, ResultInvalidHandle);
|
||||
R_UNLESS(info_sub_id < 4, ResultInvalidCombination);
|
||||
|
||||
*result = GetCurrentProcess(system.Kernel()).GetRandomEntropy(info_sub_id);
|
||||
R_SUCCEED();
|
||||
|
||||
case InfoType::InitialProcessIdRange:
|
||||
LOG_WARNING(Kernel_SVC,
|
||||
"(STUBBED) Attempted to query privileged process id bounds, returned 0");
|
||||
*result = 0;
|
||||
R_SUCCEED();
|
||||
|
||||
case InfoType::InitialProcessIdRange: {
|
||||
LOG_WARNING(Kernel_SVC, "(STUBBED) Attempted to query privileged process id bounds, returned 0/64");
|
||||
R_UNLESS(handle == InvalidHandle, ResultInvalidHandle);
|
||||
switch (InitialProcessIdRangeInfo(info_sub_id)) {
|
||||
case InitialProcessIdRangeInfo::Minimum:
|
||||
*result = 0; //todo
|
||||
R_SUCCEED();
|
||||
case InitialProcessIdRangeInfo::Maximum:
|
||||
*result = 64; //todo
|
||||
R_SUCCEED();
|
||||
default:
|
||||
R_THROW(ResultInvalidCombination);
|
||||
}
|
||||
}
|
||||
case InfoType::ThreadTickCount: {
|
||||
constexpr u64 num_cpus = 4;
|
||||
if (info_sub_id != 0xFFFFFFFFFFFFFFFF && info_sub_id >= num_cpus) {
|
||||
LOG_ERROR(Kernel_SVC, "Core count is out of range, expected {} but got {}", num_cpus,
|
||||
info_sub_id);
|
||||
LOG_ERROR(Kernel_SVC, "Core count is out of range, expected {} but got {}", num_cpus, info_sub_id);
|
||||
R_THROW(ResultInvalidCombination);
|
||||
}
|
||||
|
||||
@@ -206,8 +175,7 @@ Result GetInfo(Core::System& system, u64* result, InfoType info_id_type, Handle
|
||||
.GetHandleTable()
|
||||
.GetObject<KThread>(system.Kernel(), Handle(handle));
|
||||
if (thread.IsNull()) {
|
||||
LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle={:#08x}",
|
||||
static_cast<Handle>(handle));
|
||||
LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle={:#08x}", Handle(handle));
|
||||
R_THROW(ResultInvalidHandle);
|
||||
}
|
||||
|
||||
@@ -220,7 +188,6 @@ Result GetInfo(Core::System& system, u64* result, InfoType info_id_type, Handle
|
||||
u64 out_ticks = 0;
|
||||
if (same_thread && info_sub_id == 0xFFFFFFFFFFFFFFFF) {
|
||||
const u64 thread_ticks = current_thread->GetCpuTime();
|
||||
|
||||
out_ticks = thread_ticks + (core_timing.GetClockTicks() - prev_ctx_ticks);
|
||||
} else if (same_thread && info_sub_id == system.Kernel().CurrentPhysicalCoreIndex()) {
|
||||
out_ticks = core_timing.GetClockTicks() - prev_ctx_ticks;
|
||||
@@ -230,19 +197,40 @@ Result GetInfo(Core::System& system, u64* result, InfoType info_id_type, Handle
|
||||
R_SUCCEED();
|
||||
}
|
||||
case InfoType::IdleTickCount: {
|
||||
// Verify the input handle is invalid.
|
||||
R_UNLESS(handle == InvalidHandle, ResultInvalidHandle);
|
||||
|
||||
// Verify the requested core is valid.
|
||||
const bool core_valid =
|
||||
(info_sub_id == 0xFFFFFFFFFFFFFFFF) ||
|
||||
(info_sub_id == static_cast<u64>(system.Kernel().CurrentPhysicalCoreIndex()));
|
||||
(info_sub_id == 0xFFFFFFFFFFFFFFFF)
|
||||
|| (info_sub_id == u64(system.Kernel().CurrentPhysicalCoreIndex()));
|
||||
R_UNLESS(core_valid, ResultInvalidCombination);
|
||||
|
||||
// Verify the input handle is invalid.
|
||||
R_UNLESS(handle == InvalidHandle, ResultInvalidHandle);
|
||||
|
||||
// Get the idle tick count.
|
||||
*result = system.Kernel().CurrentScheduler()->GetIdleThread()->GetCpuTime();
|
||||
R_SUCCEED();
|
||||
}
|
||||
case InfoType::MesosphereMeta: {
|
||||
enum MesosphereMetaInfo : u64 {
|
||||
KernelVersion = 0,
|
||||
IsKTraceEnabled = 1,
|
||||
IsSingleStepEnabled = 2,
|
||||
};
|
||||
R_UNLESS(handle == InvalidHandle, ResultInvalidHandle);
|
||||
switch (MesosphereMetaInfo(info_sub_id)) {
|
||||
case MesosphereMetaInfo::KernelVersion:
|
||||
*result = Kernel::Svc::SupportedKernelVersion;
|
||||
R_SUCCEED();
|
||||
case MesosphereMetaInfo::IsKTraceEnabled:
|
||||
*result = 0;
|
||||
R_SUCCEED();
|
||||
case MesosphereMetaInfo::IsSingleStepEnabled:
|
||||
*result = 0;
|
||||
R_SUCCEED();
|
||||
default:
|
||||
R_THROW(ResultInvalidCombination);
|
||||
}
|
||||
}
|
||||
case InfoType::MesosphereCurrentProcess: {
|
||||
// Verify the input handle is invalid.
|
||||
R_UNLESS(handle == InvalidHandle, ResultInvalidHandle);
|
||||
@@ -255,13 +243,11 @@ Result GetInfo(Core::System& system, u64* result, InfoType info_id_type, Handle
|
||||
KHandleTable& handle_table = current_process->GetHandleTable();
|
||||
|
||||
// Get a new handle for the current process.
|
||||
Handle tmp;
|
||||
Handle tmp{};
|
||||
R_TRY(handle_table.Add(system.Kernel(), std::addressof(tmp), current_process));
|
||||
|
||||
// Set the output.
|
||||
*result = tmp;
|
||||
|
||||
// We succeeded.
|
||||
R_SUCCEED();
|
||||
}
|
||||
default:
|
||||
|
||||
@@ -1387,7 +1387,8 @@ public:
|
||||
{5, &ACC_U1::GetProfile, "GetProfile"},
|
||||
{6, nullptr, "GetProfileDigest"},
|
||||
{50, &ACC_U1::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"},
|
||||
{51, &ACC_U1::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"},
|
||||
{51, &ACC_U1::TrySelectUserWithoutInteractionDeprecated, "TrySelectUserWithoutInteractionDeprecated"},
|
||||
{52, &ACC_U1::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"},
|
||||
{60, &ACC_U1::ListOpenContextStoredUsers, "ListOpenContextStoredUsers"},
|
||||
{99, nullptr, "DebugActivateOpenContextRetention"},
|
||||
{100, nullptr, "GetUserRegistrationNotifier"},
|
||||
|
||||
@@ -94,9 +94,7 @@ enum class AppletId : u32 {
|
||||
LoginShare = 0x18,
|
||||
WebAuth = 0x19,
|
||||
MyPage = 0x1A,
|
||||
Lhub = 0x35,
|
||||
// Homebrew -- uses same ProgramId as qlaunch
|
||||
UlauncherUmenu = 0xF000'0000,
|
||||
Lhub = 0x35
|
||||
};
|
||||
|
||||
enum class AppletProgramId : u64 {
|
||||
@@ -156,6 +154,7 @@ enum class AppletMessage : u32 {
|
||||
DetectLongPressingCaptureButton = 91,
|
||||
AlbumScreenShotTaken = 92,
|
||||
AlbumRecordingSaved = 93,
|
||||
StartupLogoDisappeared = 95,
|
||||
};
|
||||
|
||||
enum class LibraryAppletMode : u32 {
|
||||
|
||||
@@ -91,6 +91,7 @@ struct Applet {
|
||||
// Common state
|
||||
bool sleep_lock_enabled{};
|
||||
bool vr_mode_enabled{};
|
||||
bool vr_mode_enabled_3d{};
|
||||
bool lcd_backlight_off_enabled{};
|
||||
APM::CpuBoostMode boost_mode{};
|
||||
bool request_exit_to_library_applet_at_execute_next_program_enabled{};
|
||||
|
||||
@@ -24,20 +24,6 @@ namespace Service::AM {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr size_t NroPathSize = 512;
|
||||
constexpr size_t NroArgvSize = 2048;
|
||||
constexpr size_t MenuCaptionSize = 1024;
|
||||
struct UloaderTargetInput {
|
||||
u32 magic;
|
||||
bool target_once;
|
||||
bool is_auto_game_recording;
|
||||
std::array<u8, 2> unused;
|
||||
std::array<char, NroPathSize> nro_path;
|
||||
std::array<char, NroArgvSize> nro_argv;
|
||||
std::array<char, MenuCaptionSize> menu_caption;
|
||||
};
|
||||
static_assert(sizeof(UloaderTargetInput) == 3592);
|
||||
|
||||
constexpr u32 LaunchParameterAccountPreselectedUserMagic = 0xC79497CA;
|
||||
|
||||
struct LaunchParameterAccountPreselectedUser {
|
||||
@@ -134,44 +120,6 @@ void PushInShowController(Core::System& system, AppletStorageChannel& channel) {
|
||||
channel.Push(system.Kernel(), std::make_shared<IStorage>(system, std::move(user_args_data)));
|
||||
}
|
||||
|
||||
void PushInShowUlauncherUmenu(Core::System& system, AppletStorageChannel& channel) {
|
||||
typedef std::array<u64, 2> AccountUid;
|
||||
const CommonArguments common_args = {
|
||||
.arguments_version = CommonArgumentVersion::Version3,
|
||||
.size = CommonArgumentSize::Version3,
|
||||
.library_version = 2,
|
||||
.theme_color = ThemeColor::BasicBlack,
|
||||
.play_startup_sound = true,
|
||||
.system_tick = system.CoreTiming().GetClockTicks(),
|
||||
};
|
||||
struct UmenuInput {
|
||||
AccountUid selected_user;
|
||||
UloaderTargetInput suspended_hb_target_ipt; // Set if homebrew (launched as an application) is currently suspended
|
||||
u64 suspended_app_id; // Set if any normal application is suspended
|
||||
std::array<char, 0x301> last_menu_fs_path; //FS_MAX_PATH
|
||||
std::array<char, 0x301> last_menu_path;
|
||||
u32 last_menu_index;
|
||||
bool reload_theme_cache;
|
||||
bool warned_about_outdated_theme;
|
||||
u32 last_added_app_count;
|
||||
u32 last_deleted_app_count;
|
||||
u32 in_verify_app_count;
|
||||
} user_args = {};
|
||||
static_assert(sizeof(UmenuInput) == 5176);
|
||||
|
||||
auto const user = system.GetProfileManager().GetUser(0);
|
||||
user_args.selected_user[0] = user->uuid[0];
|
||||
user_args.selected_user[1] = user->uuid[1];
|
||||
user_args.warned_about_outdated_theme = true;
|
||||
|
||||
std::vector<u8> common_args_data(sizeof(common_args));
|
||||
std::vector<u8> user_args_data(sizeof(user_args));
|
||||
std::memcpy(common_args_data.data(), std::addressof(common_args), sizeof(common_args));
|
||||
std::memcpy(user_args_data.data(), std::addressof(user_args), sizeof(user_args));
|
||||
channel.Push(std::make_shared<IStorage>(system, std::move(common_args_data)));
|
||||
channel.Push(std::make_shared<IStorage>(system, std::move(user_args_data)));
|
||||
}
|
||||
|
||||
void PushInShowCabinetData(Core::System& system, AppletStorageChannel& channel) {
|
||||
const CommonArguments arguments{
|
||||
.arguments_version = CommonArgumentVersion::Version3,
|
||||
@@ -343,18 +291,8 @@ void AppletManager::SetWindowSystem(WindowSystem* window_system) {
|
||||
applet->previous_program_index = params.previous_program_index;
|
||||
|
||||
// Push UserChannel data from previous application
|
||||
// Or ulaunch initialization where we push parameters willingly!
|
||||
if (params.launch_type == LaunchType::ApplicationInitiated) {
|
||||
applet->user_channel_launch_parameter.swap(m_system.GetUserChannel());
|
||||
} else if (params.launch_type == LaunchType::FrontendUlaunchInitiated) {
|
||||
UloaderTargetInput target_ipt = {};
|
||||
target_ipt.magic = 0x49444C55; // "ULDI"
|
||||
target_ipt.nro_path = {"sdmc:/hbmenu.nro"};
|
||||
target_ipt.menu_caption = {"Loaded by uLoader v1.2.4 - uLaunch's custom hbloader replacement ;)"};
|
||||
std::vector<u8> v(sizeof(target_ipt));
|
||||
std::memcpy(v.data(), std::addressof(target_ipt), sizeof(target_ipt));
|
||||
applet->user_channel_launch_parameter.clear();
|
||||
applet->user_channel_launch_parameter.push_back(std::move(v));
|
||||
}
|
||||
|
||||
// TODO: Read whether we need a preselected user from NACP?
|
||||
@@ -396,9 +334,6 @@ void AppletManager::SetWindowSystem(WindowSystem* window_system) {
|
||||
case AppletId::Controller:
|
||||
PushInShowController(m_system, InitializeFakeCallerApplet(m_system, applet));
|
||||
break;
|
||||
case AppletId::UlauncherUmenu:
|
||||
PushInShowUlauncherUmenu(m_system, InitializeFakeCallerApplet(m_system, applet));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -406,7 +341,7 @@ void AppletManager::SetWindowSystem(WindowSystem* window_system) {
|
||||
// Applet was started by frontend, so it is foreground.
|
||||
applet->lifecycle_manager.SetFocusState(m_system.Kernel(), FocusState::InFocus);
|
||||
|
||||
if (applet->applet_id == AppletId::QLaunch || applet->applet_id == AppletId::UlauncherUmenu) {
|
||||
if (applet->applet_id == AppletId::QLaunch) {
|
||||
applet->lifecycle_manager.SetFocusHandlingMode(false);
|
||||
applet->lifecycle_manager.SetOutOfFocusSuspendingEnabled(false);
|
||||
m_window_system->TrackApplet(applet, false);
|
||||
|
||||
@@ -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 2024 yuzu Emulator Project
|
||||
@@ -26,9 +26,6 @@ class WindowSystem;
|
||||
enum class LaunchType {
|
||||
FrontendInitiated,
|
||||
ApplicationInitiated,
|
||||
// Special masquerade for AMS + uLaunch CFW
|
||||
FrontendUlaunchInitiated = 0x800,
|
||||
FrontendUmenuInitiated,
|
||||
};
|
||||
|
||||
struct FrontendAppletParameters {
|
||||
|
||||
@@ -73,20 +73,16 @@ Result DisplayLayerManager::CreateManagedDisplayLayer(u64* out_layer_id) {
|
||||
R_TRY(m_manager_display_service->CreateManagedLayer(
|
||||
out_layer_id, 0, display_id, Service::AppletResourceUserId{m_process->GetProcessId()}));
|
||||
|
||||
m_manager_display_service->SetLayerVisibility(m_visible, *out_layer_id);
|
||||
(void)m_display_service->GetContainer()->SetLayerStackMask(*out_layer_id,
|
||||
this->GetLayerStackMask());
|
||||
|
||||
if (m_applet_id != AppletId::Application) {
|
||||
(void)m_manager_display_service->SetLayerBlending(m_blending_enabled, *out_layer_id);
|
||||
if (m_applet_id == AppletId::OverlayDisplay) {
|
||||
(void)m_manager_display_service->SetLayerZIndex(Overlay, *out_layer_id);
|
||||
(void)m_manager_display_service->SetLayerZIndex(-1, *out_layer_id);
|
||||
(void)m_display_service->GetContainer()->SetLayerIsOverlay(*out_layer_id, true);
|
||||
} else {
|
||||
(void)m_manager_display_service->SetLayerZIndex(Foreground, *out_layer_id);
|
||||
(void)m_manager_display_service->SetLayerZIndex(1, *out_layer_id);
|
||||
}
|
||||
}
|
||||
|
||||
(void)m_display_service->GetContainer()->SetLayerZIndex(*out_layer_id, true);
|
||||
m_managed_display_layers.emplace(*out_layer_id);
|
||||
|
||||
R_SUCCEED();
|
||||
@@ -126,12 +122,11 @@ Result DisplayLayerManager::IsSystemBufferSharingEnabled() {
|
||||
|
||||
// Ensure the overlay layer is visible
|
||||
m_manager_display_service->SetLayerVisibility(m_visible, m_system_shared_layer_id);
|
||||
(void)m_display_service->GetContainer()->SetLayerStackMask(m_system_shared_layer_id,
|
||||
this->GetLayerStackMask());
|
||||
m_manager_display_service->SetLayerBlending(m_blending_enabled, m_system_shared_layer_id);
|
||||
s32 initial_z = Foreground;
|
||||
s32 initial_z = 1;
|
||||
(void)m_display_service->GetContainer()->SetLayerZIndex(m_system_shared_layer_id, true);
|
||||
if (m_applet_id == AppletId::OverlayDisplay) {
|
||||
initial_z = Overlay;
|
||||
initial_z = -1;
|
||||
(void)m_display_service->GetContainer()->SetLayerIsOverlay(m_system_shared_layer_id, true);
|
||||
}
|
||||
m_manager_display_service->SetLayerZIndex(initial_z, m_system_shared_layer_id);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
@@ -73,8 +73,7 @@ Result IAllSystemAppletProxiesService::OpenLibraryAppletProxy(
|
||||
|
||||
Result IAllSystemAppletProxiesService::OpenOverlayAppletProxy(
|
||||
Out<SharedPointer<IOverlayAppletProxy>> out_overlay_applet_proxy, ClientProcessId pid,
|
||||
InCopyHandle<Kernel::KProcess> process_handle,
|
||||
InLargeData<AppletAttribute, BufferAttr_HipcMapAlias> attribute) {
|
||||
InCopyHandle<Kernel::KProcess> process_handle) {
|
||||
LOG_WARNING(Service_AM, "called");
|
||||
|
||||
if (const auto applet = this->GetAppletFromProcessId(pid); applet) {
|
||||
@@ -89,8 +88,7 @@ Result IAllSystemAppletProxiesService::OpenOverlayAppletProxy(
|
||||
|
||||
Result IAllSystemAppletProxiesService::OpenSystemApplicationProxy(
|
||||
Out<SharedPointer<IApplicationProxy>> out_system_application_proxy, ClientProcessId pid,
|
||||
InCopyHandle<Kernel::KProcess> process_handle,
|
||||
InLargeData<AppletAttribute, BufferAttr_HipcMapAlias> attribute) {
|
||||
InCopyHandle<Kernel::KProcess> process_handle) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
|
||||
if (const auto applet = this->GetAppletFromProcessId(pid); applet) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
@@ -36,15 +36,13 @@ private:
|
||||
InCopyHandle<Kernel::KProcess> process_handle,
|
||||
InLargeData<AppletAttribute, BufferAttr_HipcMapAlias> attribute);
|
||||
Result OpenOverlayAppletProxy(Out<SharedPointer<IOverlayAppletProxy>> out_overlay_applet_proxy,
|
||||
ClientProcessId pid, InCopyHandle<Kernel::KProcess> process_handle,
|
||||
InLargeData<AppletAttribute, BufferAttr_HipcMapAlias> attribute);
|
||||
ClientProcessId pid, InCopyHandle<Kernel::KProcess> process_handle);
|
||||
Result OpenLibraryAppletProxyOld(
|
||||
Out<SharedPointer<ILibraryAppletProxy>> out_library_applet_proxy, ClientProcessId pid,
|
||||
InCopyHandle<Kernel::KProcess> process_handle);
|
||||
Result OpenSystemApplicationProxy(
|
||||
Out<SharedPointer<IApplicationProxy>> out_system_application_proxy, ClientProcessId pid,
|
||||
InCopyHandle<Kernel::KProcess> process_handle,
|
||||
InLargeData<AppletAttribute, BufferAttr_HipcMapAlias> attribute);
|
||||
InCopyHandle<Kernel::KProcess> process_handle);
|
||||
Result GetSystemProcessCommonFunctions();
|
||||
Result GetAppletAlternativeFunctions();
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system_, std::shared_ptr<Ap
|
||||
{14, nullptr, "GetWakeupCount"}, //11.0.0+
|
||||
{15, nullptr, "Unknown15"}, //19.0.0+
|
||||
{20, D<&ICommonStateGetter::PushToGeneralChannel>, "PushToGeneralChannel"},
|
||||
{30, nullptr, "GetHomeButtonReaderLockAccessor"},
|
||||
{30, D<&ICommonStateGetter::GetHomeButtonReaderLockAccessor>, "GetHomeButtonReaderLockAccessor"},
|
||||
{31, D<&ICommonStateGetter::GetReaderLockAccessorEx>, "GetReaderLockAccessorEx"}, //2.0.0+
|
||||
{32, D<&ICommonStateGetter::GetWriterLockAccessorEx>, "GetWriterLockAccessorEx"}, //7.0.0+
|
||||
{40, nullptr, "GetCradleFwVersion"}, //2.0.0+
|
||||
@@ -65,7 +65,7 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system_, std::shared_ptr<Ap
|
||||
{100, D<&ICommonStateGetter::SetHandlingHomeButtonShortPressedEnabled>, "SetHandlingHomeButtonShortPressedEnabled"},
|
||||
{110, nullptr, "OpenMyGpuErrorHandler"},
|
||||
{120, D<&ICommonStateGetter::GetAppletLaunchedHistory>, "GetAppletLaunchedHistory"}, //13.0.0+
|
||||
{130, nullptr, "Unknown130"}, //21.0.0+
|
||||
{130, D<&ICommonStateGetter::EnableStartupLogoDisappearedMessage>, "EnableStartupLogoDisappearedMessage"}, //21.0.0+
|
||||
{200, D<&ICommonStateGetter::GetOperationModeSystemInfo>, "GetOperationModeSystemInfo"},
|
||||
{300, D<&ICommonStateGetter::GetSettingsPlatformRegion>, "GetSettingsPlatformRegion"},
|
||||
{400, nullptr, "ActivateMigrationService"},
|
||||
@@ -74,14 +74,17 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system_, std::shared_ptr<Ap
|
||||
{501, nullptr, "SuppressDisablingSleepTemporarily"},
|
||||
{502, nullptr, "IsSleepEnabled"},
|
||||
{503, nullptr, "IsDisablingSleepSuppressed"},
|
||||
{600, nullptr, "Unknown600"}, //20.0.0+
|
||||
{600, nullptr, "SetHidInputMagnificationForApplication"}, //20.0.0+
|
||||
{610, D<&ICommonStateGetter::Unknown610>, "Unknown610"}, //21.0.0+
|
||||
{611, D<&ICommonStateGetter::Unknown611>, "Unknown611"}, //22.0.0+
|
||||
{900, D<&ICommonStateGetter::SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled>, "SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled"}, //11.0.0+
|
||||
{910, nullptr, "GetLaunchRequiredTick"}, //17.0.0+
|
||||
{1000, nullptr, "BeginVrMode3d"}, //19.0.0+
|
||||
{1001, nullptr, "EndVrMode3d"}, //19.0.0+
|
||||
{1002, nullptr, "IsVrModeEnabled3d"}, //19.0.0+
|
||||
{1000, D<&ICommonStateGetter::BeginVrMode3d>, "BeginVrMode3d"}, //19.0.0+
|
||||
{1001, D<&ICommonStateGetter::EndVrMode3d>, "EndVrMode3d"}, //19.0.0+
|
||||
{1002, D<&ICommonStateGetter::IsVrModeEnabled3d>, "IsVrModeEnabled3d"}, //19.0.0+
|
||||
{1003, D<&ICommonStateGetter::GetVrLaboGoggleViewport>, "GetVrLaboGoggleViewport"}, //21.0.0+
|
||||
{1004, D<&ICommonStateGetter::GetPanelPhysicalSizeForSpecificTitle>, "GetPanelPhysicalSizeForSpecificTitle"}, //21.0.0+
|
||||
{1005, D<&ICommonStateGetter::GetPanelResolutionForSpecificTitle>, "GetPanelResolutionForSpecificTitle"}, //21.0.0+
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@@ -311,6 +314,12 @@ Result ICommonStateGetter::PerformSystemButtonPressingIfInFocus(SystemButtonType
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ICommonStateGetter::EnableStartupLogoDisappearedMessage() {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
m_applet->lifecycle_manager.PushUnorderedMessage(system.Kernel(), AppletMessage::StartupLogoDisappeared);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ICommonStateGetter::GetOperationModeSystemInfo(Out<u32> out_operation_mode_system_info) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
*out_operation_mode_system_info = 0;
|
||||
@@ -355,6 +364,12 @@ Result ICommonStateGetter::PushToGeneralChannel(SharedPointer<IStorage> storage)
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ICommonStateGetter::GetHomeButtonReaderLockAccessor(Out<SharedPointer<ILockAccessor>> out_lock_accessor) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
*out_lock_accessor = std::make_shared<ILockAccessor>(system);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ICommonStateGetter::SetHandlingHomeButtonShortPressedEnabled(bool enabled) {
|
||||
LOG_DEBUG(Service_AM, "called, enabled={} applet_id={}", enabled, m_applet->applet_id);
|
||||
|
||||
@@ -363,14 +378,58 @@ Result ICommonStateGetter::SetHandlingHomeButtonShortPressedEnabled(bool enabled
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ICommonStateGetter::Unknown610() {
|
||||
Result ICommonStateGetter::Unknown610(u64 unk) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ICommonStateGetter::Unknown611() {
|
||||
Result ICommonStateGetter::Unknown611(u8 unk) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ICommonStateGetter::BeginVrMode3d() {
|
||||
std::scoped_lock lk{m_applet->lock};
|
||||
m_applet->vr_mode_enabled_3d = true;
|
||||
LOG_WARNING(Service_AM, "VR Mode is {}", m_applet->vr_mode_enabled_3d ? "on" : "off");
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ICommonStateGetter::EndVrMode3d() {
|
||||
std::scoped_lock lk{m_applet->lock};
|
||||
m_applet->vr_mode_enabled_3d = false;
|
||||
LOG_WARNING(Service_AM, "VR Mode is {}", m_applet->vr_mode_enabled_3d ? "on" : "off");
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ICommonStateGetter::IsVrModeEnabled3d(Out<bool> out_is_vr_mode_enabled_3d) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
std::scoped_lock lk{m_applet->lock};
|
||||
*out_is_vr_mode_enabled_3d = m_applet->vr_mode_enabled_3d;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ICommonStateGetter::GetVrLaboGoggleViewport(Out<s32> out_x, Out<s32> out_y, Out<s32> out_width, Out<s32> out_height) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
*out_x = 0;
|
||||
*out_y = 0;
|
||||
*out_width = 1280;
|
||||
*out_height = 720;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ICommonStateGetter::GetPanelPhysicalSizeForSpecificTitle(Out<f32> out_width, Out<f32> out_height) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
*out_width = 137250.0f / 1000.0f;
|
||||
*out_height = 77200.0f / 1000.0f;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ICommonStateGetter::GetPanelResolutionForSpecificTitle(Out<s32> out_width, Out<s32> out_height) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
*out_width = 1280;
|
||||
*out_height = 720;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
} // namespace Service::AM
|
||||
|
||||
@@ -56,15 +56,23 @@ private:
|
||||
Result GetDefaultDisplayResolution(Out<s32> out_width, Out<s32> out_height);
|
||||
Result GetBuiltInDisplayType(Out<s32> out_display_type);
|
||||
Result PerformSystemButtonPressingIfInFocus(SystemButtonType type);
|
||||
Result EnableStartupLogoDisappearedMessage();
|
||||
Result GetOperationModeSystemInfo(Out<u32> out_operation_mode_system_info);
|
||||
Result GetAppletLaunchedHistory(Out<s32> out_count,
|
||||
OutArray<AppletId, BufferAttr_HipcMapAlias> out_applet_ids);
|
||||
Result GetSettingsPlatformRegion(Out<Set::PlatformRegion> out_settings_platform_region);
|
||||
Result SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled();
|
||||
Result PushToGeneralChannel(SharedPointer<IStorage> storage); // cmd 20
|
||||
Result GetHomeButtonReaderLockAccessor(Out<SharedPointer<ILockAccessor>> out_lock_accessor);
|
||||
Result SetHandlingHomeButtonShortPressedEnabled(bool enabled);
|
||||
Result Unknown610();
|
||||
Result Unknown611();
|
||||
Result Unknown610(u64 unk);
|
||||
Result Unknown611(u8 unk);
|
||||
Result BeginVrMode3d();
|
||||
Result EndVrMode3d();
|
||||
Result IsVrModeEnabled3d(Out<bool> out_is_vr_mode_enabled_3d);
|
||||
Result GetVrLaboGoggleViewport(Out<s32> out_x, Out<s32> out_y, Out<s32> out_width, Out<s32> out_height);
|
||||
Result GetPanelPhysicalSizeForSpecificTitle(Out<f32> out_width, Out<f32> out_height);
|
||||
Result GetPanelResolutionForSpecificTitle(Out<s32> out_width, Out<s32> out_height);
|
||||
|
||||
void SetCpuBoostMode(HLERequestContext& ctx);
|
||||
|
||||
|
||||
@@ -55,7 +55,6 @@ AppletProgramId AppletIdToProgramId(AppletId applet_id) {
|
||||
case AppletId::OverlayDisplay:
|
||||
return AppletProgramId::OverlayDisplay;
|
||||
case AppletId::QLaunch:
|
||||
case AppletId::UlauncherUmenu: //reuses same id as Qlaunch
|
||||
return AppletProgramId::QLaunch;
|
||||
case AppletId::Starter:
|
||||
return AppletProgramId::Starter;
|
||||
|
||||
@@ -8,23 +8,18 @@
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
#include "core/file_sys/patch_manager.h"
|
||||
#include "core/file_sys/registered_cache.h"
|
||||
#include "core/hle/result.h"
|
||||
#include "core/hle/service/acc/profile_manager.h"
|
||||
#include "core/hle/service/am/applet_data_broker.h"
|
||||
#include "core/hle/service/am/applet_manager.h"
|
||||
#include "core/hle/service/am/frontend/applets.h"
|
||||
#include "core/hle/service/am/process_creation.h"
|
||||
#include "core/hle/service/am/service/library_applet_self_accessor.h"
|
||||
#include "core/hle/service/am/service/storage.h"
|
||||
#include "core/hle/service/am/window_system.h"
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
#include "core/hle/service/filesystem/filesystem.h"
|
||||
#include "core/hle/service/glue/glue_manager.h"
|
||||
#include "core/hle/service/ns/application_manager_interface.h"
|
||||
#include "core/hle/service/ns/service_getter_interface.h"
|
||||
#include "core/hle/service/server_manager.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
#include "core/loader/loader.h"
|
||||
|
||||
namespace Service::AM {
|
||||
|
||||
@@ -49,9 +44,8 @@ AppletIdentityInfo GetCallerIdentity(Applet& applet) {
|
||||
|
||||
ILibraryAppletSelfAccessor::ILibraryAppletSelfAccessor(Core::System& system_,
|
||||
std::shared_ptr<Applet> applet)
|
||||
: ServiceFramework{system_, "ILibraryAppletSelfAccessor"}, m_applet{std::move(applet)}
|
||||
, m_broker{m_applet->caller_applet_broker}
|
||||
{
|
||||
: ServiceFramework{system_, "ILibraryAppletSelfAccessor"}, m_applet{std::move(applet)},
|
||||
m_broker{m_applet->caller_applet_broker} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, D<&ILibraryAppletSelfAccessor::PopInData>, "PopInData"},
|
||||
@@ -102,128 +96,9 @@ Result ILibraryAppletSelfAccessor::PopInData(Out<SharedPointer<IStorage>> out_st
|
||||
R_RETURN(m_broker->GetInData().Pop(system.Kernel(), out_storage));
|
||||
}
|
||||
|
||||
// uLauncher emulation
|
||||
static Result UloaderCreateApplication(Core::System& system, u64 program_id, std::shared_ptr<Applet> caller_applet) {
|
||||
// Get the program NCA from storage.
|
||||
auto& storage = system.GetContentProviderUnion();
|
||||
FileSys::VirtualFile nca_raw = storage.GetEntryRaw(program_id, FileSys::ContentRecordType::Program);
|
||||
// Ensure we retrieved a program NCA.
|
||||
R_UNLESS(nca_raw != nullptr, ResultUnknown);
|
||||
std::vector<u8> control;
|
||||
std::unique_ptr<Loader::AppLoader> loader;
|
||||
Loader::ResultStatus result;
|
||||
auto process = CreateApplicationProcess(control, loader, result, system, nca_raw, program_id, 0);
|
||||
R_UNLESS(process != nullptr, ResultUnknown);
|
||||
const auto applet = std::make_shared<Applet>(system, std::move(process), true);
|
||||
applet->program_id = program_id;
|
||||
applet->applet_id = AppletId::Application;
|
||||
applet->type = AppletType::Application;
|
||||
applet->library_applet_mode = LibraryAppletMode::AllForeground;
|
||||
|
||||
applet->caller_applet = caller_applet;
|
||||
applet->caller_applet_broker = std::make_shared<AppletDataBroker>(system);
|
||||
applet->frontend = caller_applet->frontend;
|
||||
caller_applet->child_applets.push_back(applet);
|
||||
|
||||
system.GetAppletManager().GetWindowSystem()->TrackApplet(applet, true);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ILibraryAppletSelfAccessor::PushOutData(SharedPointer<IStorage> storage) {
|
||||
LOG_INFO(Service_AM, "called");
|
||||
m_broker->GetOutData().Push(system.Kernel(), storage);
|
||||
if (m_applet->applet_id == AppletId::UlauncherUmenu) {
|
||||
enum class SystemMessage : u32 {
|
||||
Invalid,
|
||||
SetSelectedUser,
|
||||
LaunchApplication,
|
||||
ResumeApplication,
|
||||
TerminateApplication,
|
||||
LaunchHomebrewLibraryApplet,
|
||||
LaunchHomebrewApplication,
|
||||
ChooseHomebrew,
|
||||
OpenWebPage,
|
||||
OpenAlbum,
|
||||
RestartMenu,
|
||||
ReloadConfig,
|
||||
UpdateMenuPaths,
|
||||
UpdateMenuIndex,
|
||||
OpenUserPage,
|
||||
OpenMiiEdit,
|
||||
OpenAddUser,
|
||||
OpenNetConnect,
|
||||
ListAddedApplications,
|
||||
ListDeletedApplications,
|
||||
OpenCabinet,
|
||||
StartVerifyApplication,
|
||||
ListInVerifyApplications,
|
||||
NotifyWarnedAboutOutdatedTheme,
|
||||
TerminateMenu,
|
||||
OpenControllerKeyRemapping
|
||||
};
|
||||
struct CommandCommonHeader {
|
||||
u32 magic;
|
||||
u32 val;
|
||||
};
|
||||
std::shared_ptr<IStorage> req_storage;
|
||||
m_broker->GetOutData().Pop(&req_storage);
|
||||
auto req_data = req_storage->GetData();
|
||||
CommandCommonHeader req_cmd{};
|
||||
std::memcpy(&req_cmd, req_data.data(), sizeof(req_cmd));
|
||||
|
||||
LOG_WARNING(Service_AM, "uLauncher:IPC {}, {}", req_cmd.magic, req_cmd.val);
|
||||
switch (SystemMessage(req_cmd.val)) {
|
||||
case SystemMessage::SetSelectedUser:
|
||||
break;
|
||||
case SystemMessage::LaunchApplication: {
|
||||
// all applet proxies OpenSystemAppletProxy
|
||||
// applet proxy GetApplicationCreator
|
||||
// application creator CreateApplication
|
||||
u64 args_value{};
|
||||
std::memcpy(std::addressof(args_value), req_data.data() + sizeof(req_cmd), sizeof(args_value));
|
||||
LOG_WARNING(Service_AM, "program_id={:016x}", args_value);
|
||||
UloaderCreateApplication(system, args_value, m_applet);
|
||||
break;
|
||||
}
|
||||
case SystemMessage::ResumeApplication:
|
||||
case SystemMessage::TerminateApplication:
|
||||
case SystemMessage::LaunchHomebrewLibraryApplet:
|
||||
case SystemMessage::LaunchHomebrewApplication:
|
||||
case SystemMessage::ChooseHomebrew:
|
||||
case SystemMessage::OpenWebPage:
|
||||
case SystemMessage::OpenAlbum:
|
||||
case SystemMessage::RestartMenu:
|
||||
case SystemMessage::ReloadConfig:
|
||||
case SystemMessage::UpdateMenuPaths:
|
||||
case SystemMessage::UpdateMenuIndex:
|
||||
case SystemMessage::OpenUserPage:
|
||||
case SystemMessage::OpenMiiEdit:
|
||||
case SystemMessage::OpenAddUser:
|
||||
case SystemMessage::OpenNetConnect:
|
||||
case SystemMessage::ListAddedApplications:
|
||||
case SystemMessage::ListDeletedApplications:
|
||||
case SystemMessage::OpenCabinet:
|
||||
case SystemMessage::StartVerifyApplication:
|
||||
case SystemMessage::ListInVerifyApplications:
|
||||
case SystemMessage::NotifyWarnedAboutOutdatedTheme:
|
||||
break;
|
||||
case SystemMessage::TerminateMenu:
|
||||
system.GetUserChannel() = m_applet->user_channel_launch_parameter;
|
||||
system.ExecuteProgram(0);
|
||||
break;
|
||||
case SystemMessage::OpenControllerKeyRemapping:
|
||||
break;
|
||||
case SystemMessage::Invalid:
|
||||
break;
|
||||
}
|
||||
|
||||
CommandCommonHeader res_cmd{};
|
||||
std::vector<u8> res_data(0x8000);
|
||||
res_cmd.magic = 0x21494D53;
|
||||
res_cmd.val = u32(ResultSuccess.raw);
|
||||
std::memcpy(res_data.data(), &res_cmd, sizeof(res_cmd));
|
||||
m_broker->GetInData().Push(std::make_shared<IStorage>(system, std::move(res_data)));
|
||||
}
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -26,17 +29,14 @@ IStorage::~IStorage() = default;
|
||||
|
||||
Result IStorage::Open(Out<SharedPointer<IStorageAccessor>> out_storage_accessor) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
|
||||
R_UNLESS(m_impl->GetHandle() == nullptr, AM::ResultInvalidStorageType);
|
||||
|
||||
*out_storage_accessor = std::make_shared<IStorageAccessor>(system, m_impl);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IStorage::OpenTransferStorage(
|
||||
Out<SharedPointer<ITransferStorageAccessor>> out_transfer_storage_accessor) {
|
||||
Result IStorage::OpenTransferStorage(Out<SharedPointer<ITransferStorageAccessor>> out_transfer_storage_accessor) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
R_UNLESS(m_impl->GetHandle() != nullptr, AM::ResultInvalidStorageType);
|
||||
|
||||
*out_transfer_storage_accessor = std::make_shared<ITransferStorageAccessor>(system, m_impl);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ void WindowSystem::TrackApplet(std::shared_ptr<Applet> applet, bool is_applicati
|
||||
{
|
||||
std::scoped_lock lk{m_lock};
|
||||
|
||||
if (applet->applet_id == AppletId::QLaunch || applet->applet_id == AppletId::UlauncherUmenu)) {
|
||||
if (applet->applet_id == AppletId::QLaunch) {
|
||||
ASSERT(m_home_menu == nullptr);
|
||||
m_home_menu = applet.get();
|
||||
} else if (applet->applet_id == AppletId::OverlayDisplay) {
|
||||
|
||||
@@ -798,7 +798,7 @@ void LoopProcess(Core::System& system) {
|
||||
const auto FileSystemProxyFactory = [&] { return std::make_shared<FSP_SRV>(system); };
|
||||
|
||||
server_manager->RegisterNamedService("fsp-ldr", std::make_shared<FSP_LDR>(system));
|
||||
server_manager->RegisterNamedService("fsp:pr", std::make_shared<FSP_PR>(system));
|
||||
server_manager->RegisterNamedService("fsp-pr", std::make_shared<FSP_PR>(system));
|
||||
server_manager->RegisterNamedService("fsp-srv", std::move(FileSystemProxyFactory));
|
||||
ServerManager::RunServer(std::move(server_manager));
|
||||
}
|
||||
|
||||
@@ -32,7 +32,6 @@ public:
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
~I2CSession() override = default;
|
||||
|
||||
Result Send(InBuffer<BufferAttr_HipcMapAlias> in_data, u32 transaction_option) {
|
||||
LOG_WARNING(Service, "(stubbed) topt={}", transaction_option);
|
||||
R_THROW(ResultUnknown);
|
||||
@@ -50,21 +49,40 @@ public:
|
||||
: ServiceFramework{system_, "i2c"}
|
||||
{
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "OpenSessionForDev"},
|
||||
{0, C<&I2C::OpenSessionForDev>, "OpenSessionForDev"},
|
||||
{1, C<&I2C::OpenSession>, "OpenSession"},
|
||||
{2, nullptr, "HasDevice"},
|
||||
{3, nullptr, "HasDeviceForDev"},
|
||||
{4, nullptr, "OpenSession2"},
|
||||
{2, C<&I2C::HasDevice>, "HasDevice"},
|
||||
{3, C<&I2C::HasDeviceForDev>, "HasDeviceForDev"},
|
||||
{4, C<&I2C::OpenSession2>, "OpenSession2"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
~I2C() override = default;
|
||||
|
||||
Result OpenSession(I2CDevice device, OutInterface<I2CSession> out_session) {
|
||||
Result OpenSessionForDev(OutInterface<I2CSession> out_session, s32 bus_idx, u32 slave_address, u32 addressing_mode, u32 speed_mode) {
|
||||
LOG_DEBUG(Service, "(stubbed)");
|
||||
*out_session = std::make_shared<I2CSession>(system);
|
||||
R_SUCCEED();
|
||||
}
|
||||
Result OpenSession(OutInterface<I2CSession> out_session, I2CDevice device) {
|
||||
LOG_DEBUG(Service, "(stubbed)");
|
||||
*out_session = std::make_shared<I2CSession>(system);
|
||||
R_SUCCEED();
|
||||
}
|
||||
Result HasDevice(Out<bool> out_has_device, I2CDevice device) {
|
||||
LOG_DEBUG(Service, "(stubbed)");
|
||||
*out_has_device = false;
|
||||
R_SUCCEED();
|
||||
}
|
||||
Result HasDeviceForDev(Out<bool> out_has_device, I2CDevice device) {
|
||||
LOG_DEBUG(Service, "(stubbed)");
|
||||
*out_has_device = false;
|
||||
R_SUCCEED();
|
||||
}
|
||||
Result OpenSession2(OutInterface<I2CSession> out_session, u32 device_code) {
|
||||
LOG_DEBUG(Service, "(stubbed) device_code={}", device_code);
|
||||
*out_session = std::make_shared<I2CSession>(system);
|
||||
R_SUCCEED();
|
||||
}
|
||||
};
|
||||
|
||||
void LoopProcess(Core::System& system) {
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
#include "core/hle/service/ldn/ldn_results.h"
|
||||
#include "core/hle/service/ldn/client_process_monitor.h"
|
||||
#include "core/hle/service/ipc_helpers.h"
|
||||
|
||||
namespace Service::LDN {
|
||||
|
||||
IClientProcessMonitor::IClientProcessMonitor(Core::System& system_)
|
||||
: ServiceFramework{system_, "IClientProcessMonitor"} {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, D<&IClientProcessMonitor::RegisterClient>, "RegisterClient"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
IClientProcessMonitor::~IClientProcessMonitor() = default;
|
||||
|
||||
Result IClientProcessMonitor::RegisterClient() {
|
||||
LOG_WARNING(Service_LDN, "(STUBBED) called");
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
} // namespace Service::LDN
|
||||
@@ -1,25 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/cmif_types.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Service::LDN {
|
||||
|
||||
class IClientProcessMonitor final
|
||||
: public ServiceFramework<IClientProcessMonitor> {
|
||||
public:
|
||||
explicit IClientProcessMonitor(Core::System& system_);
|
||||
~IClientProcessMonitor() override;
|
||||
|
||||
private:
|
||||
Result RegisterClient();
|
||||
};
|
||||
|
||||
} // namespace Service::LDN
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
@@ -7,7 +7,6 @@
|
||||
#include "core/core.h"
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
#include "core/hle/service/ldn/ldn.h"
|
||||
#include "core/hle/service/ldn/client_process_monitor.h"
|
||||
#include "core/hle/service/ldn/monitor_service.h"
|
||||
#include "core/hle/service/ldn/sf_monitor_service.h"
|
||||
#include "core/hle/service/ldn/sf_service.h"
|
||||
@@ -17,6 +16,26 @@
|
||||
|
||||
namespace Service::LDN {
|
||||
|
||||
class IClientProcessMonitor final
|
||||
: public ServiceFramework<IClientProcessMonitor> {
|
||||
public:
|
||||
explicit IClientProcessMonitor(Core::System& system_)
|
||||
: ServiceFramework{system_, "IClientProcessMonitor"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, D<&IClientProcessMonitor::RegisterClient>, "RegisterClient"},
|
||||
};
|
||||
// clang-format on
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
~IClientProcessMonitor() override = default;
|
||||
private:
|
||||
Result RegisterClient(ClientProcessId pid) {
|
||||
LOG_WARNING(Service_LDN, "(STUBBED) called");
|
||||
R_SUCCEED();
|
||||
}
|
||||
};
|
||||
|
||||
class IMonitorServiceCreator final : public ServiceFramework<IMonitorServiceCreator> {
|
||||
public:
|
||||
explicit IMonitorServiceCreator(Core::System& system_) : ServiceFramework{system_, "ldn:m"} {
|
||||
@@ -25,7 +44,6 @@ public:
|
||||
{0, C<&IMonitorServiceCreator::CreateMonitorService>, "CreateMonitorService"}
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
|
||||
@@ -139,7 +139,7 @@ IApplicationManagerInterface::IApplicationManagerInterface(Core::System& system_
|
||||
{405, nullptr, "ListApplicationControlCacheEntryInfo"},
|
||||
{406, nullptr, "GetApplicationControlProperty"},
|
||||
{407, &IApplicationManagerInterface::ListApplicationTitle, "ListApplicationTitle"},
|
||||
{408, nullptr, "ListApplicationIcon"},
|
||||
{408, &IApplicationManagerInterface::ListApplicationIcon, "ListApplicationIcon"},
|
||||
{411, nullptr, "Unknown411"}, //19.0.0+
|
||||
{412, nullptr, "Unknown412"}, //19.0.0+
|
||||
{413, nullptr, "Unknown413"}, //19.0.0+
|
||||
@@ -894,4 +894,9 @@ void IApplicationManagerInterface::ListApplicationTitle(HLERequestContext& ctx)
|
||||
IReadOnlyApplicationControlDataInterface(system).ListApplicationTitle(ctx);
|
||||
}
|
||||
|
||||
void IApplicationManagerInterface::ListApplicationIcon(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_NS, "called");
|
||||
IReadOnlyApplicationControlDataInterface(system).ListApplicationIcon(ctx);
|
||||
}
|
||||
|
||||
} // namespace Service::NS
|
||||
|
||||
@@ -78,6 +78,7 @@ public:
|
||||
|
||||
void PushApplicationRecord(HLERequestContext& ctx);
|
||||
void ListApplicationTitle(HLERequestContext& ctx);
|
||||
void ListApplicationIcon(HLERequestContext& ctx);
|
||||
|
||||
private:
|
||||
KernelHelpers::ServiceContext service_context;
|
||||
|
||||
@@ -10,7 +10,9 @@
|
||||
#include "core/hle/service/ns/query_service.h"
|
||||
#include "core/hle/service/ns/service_getter_interface.h"
|
||||
#include "core/hle/service/ns/system_update_interface.h"
|
||||
#include "core/hle/service/ns/vulnerability_manager_interface.h"
|
||||
#include "core/hle/service/ipc_helpers.h"
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
#include "core/hle/service/cmif_types.h"
|
||||
#include "core/hle/service/server_manager.h"
|
||||
|
||||
namespace Service::NS {
|
||||
@@ -39,6 +41,43 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class IVulnerabilityManagerInterface final
|
||||
: public ServiceFramework<IVulnerabilityManagerInterface> {
|
||||
public:
|
||||
explicit IVulnerabilityManagerInterface(Core::System& system_)
|
||||
: ServiceFramework{system_, "ns:vm"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{1200, D<&IVulnerabilityManagerInterface::NeedsUpdateVulnerability>, "NeedsUpdateVulnerability"},
|
||||
{1201, nullptr, "UpdateSafeSystemVersionForDebug"},
|
||||
{1202, nullptr, "GetSafeSystemVersion"},
|
||||
{3100, D<&IVulnerabilityManagerInterface::GetSafeSystemVersionCheckInfo>, "GetSafeSystemVersionCheckInfo"},
|
||||
{3101, nullptr, "RequestUpdateSafeSystemVersionCheckInfo"},
|
||||
{3102, D<&IVulnerabilityManagerInterface::ResetSafeSystemVersionCheckInfo>, "ResetSafeSystemVersionCheckInfo"},
|
||||
};
|
||||
// clang-format on
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
~IVulnerabilityManagerInterface() override = default;
|
||||
|
||||
Result NeedsUpdateVulnerability(Out<bool> out_needs_update_vulnerability) {
|
||||
LOG_WARNING(Service_NS, "(STUBBED)");
|
||||
*out_needs_update_vulnerability = false;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetSafeSystemVersionCheckInfo(Out<std::array<u8, 0x20>> out_system_version_check_info) {
|
||||
LOG_WARNING(Service_NS, "(STUBBED)");
|
||||
*out_system_version_check_info = {};
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ResetSafeSystemVersionCheckInfo() {
|
||||
LOG_WARNING(Service_NS, "(STUBBED)");
|
||||
R_SUCCEED();
|
||||
}
|
||||
};
|
||||
|
||||
void LoopProcess(Core::System& system) {
|
||||
auto server_manager = std::make_unique<ServerManager>(system);
|
||||
|
||||
|
||||
@@ -10,13 +10,17 @@
|
||||
#include <string>
|
||||
|
||||
#include "common/stb.h"
|
||||
#include "common/logging.h"
|
||||
#include "common/settings.h"
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
#include "core/file_sys/patch_manager.h"
|
||||
#include "core/file_sys/vfs/vfs.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/kernel/k_transfer_memory.h"
|
||||
#include "core/hle/result.h"
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
#include "core/hle/service/cmif_types.h"
|
||||
#include "core/hle/service/hle_ipc.h"
|
||||
#include "core/hle/service/ns/language.h"
|
||||
#include "core/hle/service/ns/ns_types.h"
|
||||
#include "core/hle/service/ns/ns_results.h"
|
||||
@@ -72,24 +76,26 @@ void SanitizeJPEGImageSize(std::vector<u8>& image) {
|
||||
|
||||
// IAsyncValue implementation for ListApplicationTitle
|
||||
// https://switchbrew.org/wiki/NS_services#ListApplicationTitle
|
||||
class IAsyncValueForListApplicationTitle final : public ServiceFramework<IAsyncValueForListApplicationTitle> {
|
||||
class IAsyncValue final : public ServiceFramework<IAsyncValue> {
|
||||
public:
|
||||
explicit IAsyncValueForListApplicationTitle(Core::System& system_, s32 offset, s32 size)
|
||||
: ServiceFramework{system_, "IAsyncValue"}, service_context{system_, "IAsyncValue"},
|
||||
data_offset{offset}, data_size{size} {
|
||||
explicit IAsyncValue(Core::System& system_, s32 offset, s32 size)
|
||||
: ServiceFramework{system_, "IAsyncValue"}
|
||||
, service_context{system_, "IAsyncValue"}
|
||||
, data_offset{offset}
|
||||
, data_size{size}
|
||||
{
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IAsyncValueForListApplicationTitle::GetSize, "GetSize"},
|
||||
{1, &IAsyncValueForListApplicationTitle::Get, "Get"},
|
||||
{2, &IAsyncValueForListApplicationTitle::Cancel, "Cancel"},
|
||||
{3, &IAsyncValueForListApplicationTitle::GetErrorContext, "GetErrorContext"},
|
||||
{0, D<&IAsyncValue::GetSize>, "GetSize"},
|
||||
{1, D<&IAsyncValue::Get>, "Get"},
|
||||
{2, D<&IAsyncValue::Cancel>, "Cancel"},
|
||||
{3, D<&IAsyncValue::GetErrorContext>, "GetErrorContext"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
|
||||
completion_event = service_context.CreateEvent("IAsyncValue:Completion");
|
||||
completion_event->GetReadableEvent().Signal(system.Kernel());
|
||||
}
|
||||
|
||||
~IAsyncValueForListApplicationTitle() override {
|
||||
~IAsyncValue() override {
|
||||
service_context.CloseEvent(completion_event);
|
||||
}
|
||||
|
||||
@@ -98,35 +104,24 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
void GetSize(HLERequestContext& ctx) {
|
||||
Result GetSize(Out<s64> out_data_size) {
|
||||
LOG_DEBUG(Service_NS, "called");
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push<s64>(data_size);
|
||||
*out_data_size = data_size;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void Get(HLERequestContext& ctx) {
|
||||
Result Get(OutBuffer<BufferAttr_HipcMapAlias> out_data_offset) {
|
||||
LOG_DEBUG(Service_NS, "called");
|
||||
std::vector<u8> buffer(sizeof(s32));
|
||||
std::memcpy(buffer.data(), &data_offset, sizeof(s32));
|
||||
ctx.WriteBuffer(buffer);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
std::memcpy(out_data_offset.data(), &data_offset, sizeof(s32));
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void Cancel(HLERequestContext& ctx) {
|
||||
Result Cancel() {
|
||||
LOG_DEBUG(Service_NS, "called");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void GetErrorContext(HLERequestContext& ctx) {
|
||||
Result GetErrorContext() {
|
||||
LOG_DEBUG(Service_NS, "called");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
KernelHelpers::ServiceContext service_context;
|
||||
Kernel::KEvent* completion_event{};
|
||||
s32 data_offset;
|
||||
@@ -144,6 +139,7 @@ IReadOnlyApplicationControlDataInterface::IReadOnlyApplicationControlDataInterfa
|
||||
{3, nullptr, "ConvertLanguageCodeToApplicationLanguage"},
|
||||
{4, nullptr, "SelectApplicationDesiredLanguage"},
|
||||
{5, D<&IReadOnlyApplicationControlDataInterface::GetApplicationControlData2>, "GetApplicationControlData"},
|
||||
{10, &IReadOnlyApplicationControlDataInterface::ListApplicationIcon, "ListApplicationIcon"},
|
||||
{13, &IReadOnlyApplicationControlDataInterface::ListApplicationTitle, "ListApplicationTitle"},
|
||||
{19, D<&IReadOnlyApplicationControlDataInterface::GetApplicationControlData3>, "GetApplicationControlData"},
|
||||
};
|
||||
@@ -160,8 +156,7 @@ Result IReadOnlyApplicationControlDataInterface::GetApplicationControlData(
|
||||
LOG_INFO(Service_NS, "called with control_source={}, application_id={:016X}",
|
||||
application_control_source, application_id);
|
||||
|
||||
const FileSys::PatchManager pm{application_id, system.GetFileSystemController(),
|
||||
system.GetContentProvider()};
|
||||
const FileSys::PatchManager pm{application_id, system.GetFileSystemController(), system.GetContentProvider()};
|
||||
const auto control = pm.GetControlMetadata();
|
||||
const auto size = out_buffer.size();
|
||||
|
||||
@@ -169,8 +164,7 @@ Result IReadOnlyApplicationControlDataInterface::GetApplicationControlData(
|
||||
const auto total_size = sizeof(FileSys::RawNACP) + icon_size;
|
||||
|
||||
if (size < total_size) {
|
||||
LOG_ERROR(Service_NS, "output buffer is too small! (actual={:016X}, expected_min=0x4000)",
|
||||
size);
|
||||
LOG_ERROR(Service_NS, "output buffer is too small! (actual={:016X}, expected_min=0x4000)", size);
|
||||
R_THROW(ResultUnknown);
|
||||
}
|
||||
|
||||
@@ -178,8 +172,7 @@ Result IReadOnlyApplicationControlDataInterface::GetApplicationControlData(
|
||||
const auto bytes = control.first->GetRawBytes();
|
||||
std::memcpy(out_buffer.data(), bytes.data(), bytes.size());
|
||||
} else {
|
||||
LOG_WARNING(Service_NS, "missing NACP data for application_id={:016X}, defaulting to zero",
|
||||
application_id);
|
||||
LOG_WARNING(Service_NS, "missing NACP data for application_id={:016X}, defaulting to zero", application_id);
|
||||
std::memset(out_buffer.data(), 0, sizeof(FileSys::RawNACP));
|
||||
}
|
||||
|
||||
@@ -204,15 +197,12 @@ Result IReadOnlyApplicationControlDataInterface::GetApplicationDesiredLanguage(
|
||||
// Convert to application language, get priority list
|
||||
const auto application_language = ConvertToApplicationLanguage(language_code);
|
||||
if (application_language == std::nullopt) {
|
||||
LOG_ERROR(Service_NS, "Could not convert application language! language_code={}",
|
||||
language_code);
|
||||
LOG_ERROR(Service_NS, "Could not convert application language! language_code={}", language_code);
|
||||
R_THROW(Service::NS::ResultApplicationLanguageNotFound);
|
||||
}
|
||||
const auto priority_list = GetApplicationLanguagePriorityList(*application_language);
|
||||
if (!priority_list) {
|
||||
LOG_ERROR(Service_NS,
|
||||
"Could not find application language priorities! application_language={}",
|
||||
*application_language);
|
||||
LOG_ERROR(Service_NS, "Could not find application language priorities! application_language={}", *application_language);
|
||||
R_THROW(Service::NS::ResultApplicationLanguageNotFound);
|
||||
}
|
||||
|
||||
@@ -256,8 +246,7 @@ Result IReadOnlyApplicationControlDataInterface::GetApplicationControlData2(
|
||||
const auto nacp_size = sizeof(FileSys::RawNACP);
|
||||
|
||||
if (size < nacp_size) {
|
||||
LOG_ERROR(Service_NS, "output buffer is too small! (actual={:016X}, expected_min={:08X})",
|
||||
size, nacp_size);
|
||||
LOG_ERROR(Service_NS, "output buffer is too small! (actual={:016X}, expected_min={:08X})", size, nacp_size);
|
||||
R_THROW(ResultUnknown);
|
||||
}
|
||||
|
||||
@@ -308,63 +297,81 @@ Result IReadOnlyApplicationControlDataInterface::GetApplicationControlData2(
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
|
||||
void IReadOnlyApplicationControlDataInterface::ListApplicationTitle(HLERequestContext& ctx) {
|
||||
/*
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto control_source = rp.PopRaw<u8>();
|
||||
rp.Skip(7, false);
|
||||
auto transfer_memory_size = rp.Pop<u64>();
|
||||
*/
|
||||
|
||||
void IReadOnlyApplicationControlDataInterface::ListApplicationIcon(HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_NS, "(stubbed)");
|
||||
const auto app_ids_buffer = ctx.ReadBuffer();
|
||||
const size_t app_count = app_ids_buffer.size() / sizeof(u64);
|
||||
|
||||
std::vector<u64> application_ids(app_count);
|
||||
if (app_count > 0) {
|
||||
std::memcpy(application_ids.data(), app_ids_buffer.data(), app_count * sizeof(u64));
|
||||
}
|
||||
|
||||
const u64 app_count = app_ids_buffer.size() / sizeof(u64);
|
||||
auto t_mem_obj = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(ctx.GetCopyHandle(0));
|
||||
auto* t_mem = t_mem_obj.GetPointerUnsafe();
|
||||
|
||||
constexpr size_t title_entry_size = sizeof(FileSys::LanguageEntry);
|
||||
const size_t total_data_size = app_count * title_entry_size;
|
||||
|
||||
constexpr s32 data_offset = 0;
|
||||
|
||||
size_t out_length = 0;
|
||||
if (t_mem != nullptr && t_mem->GetOwner() != nullptr && app_count > 0) {
|
||||
auto& memory = t_mem->GetOwner()->GetMemory();
|
||||
const auto t_mem_address = t_mem->GetSourceAddress();
|
||||
|
||||
// u64 - app count
|
||||
memory.WriteBlock(t_mem_address + out_length, &app_count, sizeof(u64));
|
||||
out_length += sizeof(u64);
|
||||
// [list of u64] - size of icons
|
||||
for (size_t i = 0; i < app_count; ++i) {
|
||||
const u64 app_id = application_ids[i];
|
||||
const FileSys::PatchManager pm{app_id, system.GetFileSystemController(),
|
||||
system.GetContentProvider()};
|
||||
const u64 app_id = app_ids_buffer[i];
|
||||
const FileSys::PatchManager pm{app_id, system.GetFileSystemController(), system.GetContentProvider()};
|
||||
const auto control = pm.GetControlMetadata();
|
||||
|
||||
FileSys::LanguageEntry entry{};
|
||||
if (control.first != nullptr) {
|
||||
entry = control.first->GetLanguageEntry();
|
||||
u64 full_size = control.second->GetSize();
|
||||
memory.WriteBlock(t_mem_address + out_length, &full_size, sizeof(u64));
|
||||
out_length += sizeof(u64);
|
||||
}
|
||||
// [list of raw icon data]
|
||||
std::vector<u8> full_icon_data;
|
||||
for (size_t i = 0; i < app_count; ++i) {
|
||||
const u64 app_id = app_ids_buffer[i];
|
||||
const FileSys::PatchManager pm{app_id, system.GetFileSystemController(), system.GetContentProvider()};
|
||||
const auto control = pm.GetControlMetadata();
|
||||
auto const full_size = control.second->GetSize();
|
||||
if (full_size > 0) {
|
||||
full_icon_data.resize(full_size);
|
||||
control.second->Read(full_icon_data.data(), full_size, 0);
|
||||
memory.WriteBlock(t_mem_address + out_length, full_icon_data.data(), full_size);
|
||||
out_length += full_size;
|
||||
}
|
||||
|
||||
const size_t offset = i * title_entry_size;
|
||||
memory.WriteBlock(t_mem_address + offset, &entry, title_entry_size);
|
||||
}
|
||||
}
|
||||
|
||||
auto async_value = std::make_shared<IAsyncValueForListApplicationTitle>(
|
||||
system, data_offset, static_cast<s32>(total_data_size));
|
||||
|
||||
auto async_value = std::make_shared<IAsyncValue>(system, 0, s32(out_length));
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(ctx, async_value->ReadableEvent());
|
||||
rb.PushIpcInterface(ctx, std::move(async_value));
|
||||
}
|
||||
|
||||
Result IReadOnlyApplicationControlDataInterface::GetApplicationControlData3(
|
||||
OutBuffer<BufferAttr_HipcMapAlias> out_buffer, Out<u32> out_flags_a, Out<u32> out_flags_b,
|
||||
Out<u32> out_actual_size, ApplicationControlSource application_control_source, u8 flag1, u8 flag2, u64 application_id) {
|
||||
void IReadOnlyApplicationControlDataInterface::ListApplicationTitle(HLERequestContext& ctx) {
|
||||
const auto app_ids_buffer = ctx.ReadBuffer();
|
||||
const size_t app_count = app_ids_buffer.size() / sizeof(u64);
|
||||
auto t_mem_obj = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(ctx.GetCopyHandle(0));
|
||||
auto* t_mem = t_mem_obj.GetPointerUnsafe();
|
||||
constexpr size_t title_entry_size = sizeof(FileSys::LanguageEntry);
|
||||
const size_t total_data_size = app_count * title_entry_size;
|
||||
constexpr s32 data_offset = 0;
|
||||
if (t_mem != nullptr && app_count > 0) {
|
||||
auto& memory = system.ApplicationMemory();
|
||||
const auto t_mem_address = t_mem->GetSourceAddress();
|
||||
for (size_t i = 0; i < app_count; ++i) {
|
||||
const u64 app_id = app_ids_buffer[i];
|
||||
const FileSys::PatchManager pm{app_id, system.GetFileSystemController(), system.GetContentProvider()};
|
||||
const auto control = pm.GetControlMetadata();
|
||||
FileSys::LanguageEntry entry{};
|
||||
if (control.first != nullptr) {
|
||||
entry = control.first->GetLanguageEntry();
|
||||
}
|
||||
const size_t offset = i * title_entry_size;
|
||||
memory.WriteBlock(t_mem_address + offset, &entry, title_entry_size);
|
||||
}
|
||||
}
|
||||
auto async_value = std::make_shared<IAsyncValue>(system, data_offset, s32(total_data_size));
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(ctx, async_value->ReadableEvent());
|
||||
rb.PushIpcInterface(ctx, std::move(async_value));
|
||||
}
|
||||
|
||||
Result IReadOnlyApplicationControlDataInterface::GetApplicationControlData3(OutBuffer<BufferAttr_HipcMapAlias> out_buffer, Out<u32> out_flags_a, Out<u32> out_flags_b, Out<u32> out_actual_size, ApplicationControlSource application_control_source, u8 flag1, u8 flag2, u64 application_id) {
|
||||
LOG_INFO(Service_NS, "called with control_source={}, flags=({:02X},{:02X}), application_id={:016X}",
|
||||
application_control_source, flag1, flag2, application_id);
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ public:
|
||||
u8 flag1,
|
||||
u8 flag2,
|
||||
u64 application_id);
|
||||
void ListApplicationIcon(HLERequestContext& ctx);
|
||||
void ListApplicationTitle(HLERequestContext& ctx);
|
||||
Result GetApplicationControlData3(
|
||||
OutBuffer<BufferAttr_HipcMapAlias> out_buffer,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
@@ -16,10 +16,8 @@ IReadOnlyApplicationRecordInterface::IReadOnlyApplicationRecordInterface(Core::S
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, D<&IReadOnlyApplicationRecordInterface::HasApplicationRecord>, "HasApplicationRecord"},
|
||||
{1, nullptr, "NotifyApplicationFailure"},
|
||||
{2, D<&IReadOnlyApplicationRecordInterface::IsDataCorruptedResult>,
|
||||
"IsDataCorruptedResult"},
|
||||
{3, D<&IReadOnlyApplicationRecordInterface::ListApplicationRecord>,
|
||||
"ListApplicationRecord"},
|
||||
{2, D<&IReadOnlyApplicationRecordInterface::IsDataCorruptedResult>, "IsDataCorruptedResult"},
|
||||
{3, D<&IReadOnlyApplicationRecordInterface::ListApplicationRecord>, "ListApplicationRecord"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
#include "core/hle/service/ns/vulnerability_manager_interface.h"
|
||||
|
||||
namespace Service::NS {
|
||||
|
||||
IVulnerabilityManagerInterface::IVulnerabilityManagerInterface(Core::System& system_)
|
||||
: ServiceFramework{system_, "ns:vm"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{1200, D<&IVulnerabilityManagerInterface::NeedsUpdateVulnerability>, "NeedsUpdateVulnerability"},
|
||||
{1201, nullptr, "UpdateSafeSystemVersionForDebug"},
|
||||
{1202, nullptr, "GetSafeSystemVersion"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
IVulnerabilityManagerInterface::~IVulnerabilityManagerInterface() = default;
|
||||
|
||||
Result IVulnerabilityManagerInterface::NeedsUpdateVulnerability(
|
||||
Out<bool> out_needs_update_vulnerability) {
|
||||
LOG_WARNING(Service_NS, "(STUBBED) called");
|
||||
*out_needs_update_vulnerability = false;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
} // namespace Service::NS
|
||||
@@ -1,21 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/cmif_types.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Service::NS {
|
||||
|
||||
class IVulnerabilityManagerInterface final
|
||||
: public ServiceFramework<IVulnerabilityManagerInterface> {
|
||||
public:
|
||||
explicit IVulnerabilityManagerInterface(Core::System& system_);
|
||||
~IVulnerabilityManagerInterface() override;
|
||||
|
||||
private:
|
||||
Result NeedsUpdateVulnerability(Out<bool> out_needs_update_vulnerability);
|
||||
};
|
||||
|
||||
} // namespace Service::NS
|
||||
@@ -66,7 +66,6 @@
|
||||
#include "core/hle/service/ssl/ssl.h"
|
||||
#include "core/hle/service/wlan/wlan.h"
|
||||
#include "core/hle/service/tma/tma.h"
|
||||
#include "core/hle/service/ulsf/ulsf.h"
|
||||
#include "core/hle/service/usb/usb.h"
|
||||
#include "core/hle/service/vi/vi.h"
|
||||
|
||||
@@ -153,8 +152,7 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system
|
||||
{"tma", &TMA::LoopProcess},
|
||||
{"usb", &USB::LoopProcess},
|
||||
{"i2c", &I2C::LoopProcess},
|
||||
{"gpio", &GPIO::LoopProcess},
|
||||
{"ulsf", &ULSF::LoopProcess},
|
||||
{"gpio", &GPIO::LoopProcess},
|
||||
})
|
||||
kernel.RunOnGuestCoreProcess(std::string(e.first), [&system, f = e.second] { f(system); });
|
||||
}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -30,15 +33,15 @@ struct DeviceSettings {
|
||||
INSERT_PADDING_BYTES(0x20); // Reserved
|
||||
|
||||
// nn::settings::system::ConsoleSixAxisSensorAccelerationBias
|
||||
Common::Vec3<f32> console_six_axis_sensor_acceleration_bias;
|
||||
Common::Vec<f32, 3> console_six_axis_sensor_acceleration_bias;
|
||||
// nn::settings::system::ConsoleSixAxisSensorAngularVelocityBias
|
||||
Common::Vec3<f32> console_six_axis_sensor_angular_velocity_bias;
|
||||
Common::Vec<f32, 3> console_six_axis_sensor_angular_velocity_bias;
|
||||
// nn::settings::system::ConsoleSixAxisSensorAccelerationGain
|
||||
std::array<u8, 0x24> console_six_axis_sensor_acceleration_gain;
|
||||
// nn::settings::system::ConsoleSixAxisSensorAngularVelocityGain
|
||||
std::array<u8, 0x24> console_six_axis_sensor_angular_velocity_gain;
|
||||
// nn::settings::system::ConsoleSixAxisSensorAngularVelocityTimeBias
|
||||
Common::Vec3<f32> console_six_axis_sensor_angular_velocity_time_bias;
|
||||
Common::Vec<f32, 3> console_six_axis_sensor_angular_velocity_time_bias;
|
||||
// nn::settings::system::ConsoleSixAxisSensorAngularAcceleration
|
||||
std::array<u8, 0x24> console_six_axis_sensor_angular_acceleration;
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
@@ -153,15 +153,15 @@ struct SystemSettings {
|
||||
INSERT_PADDING_BYTES(0x7FF8); // Reserved
|
||||
|
||||
// nn::settings::system::ConsoleSixAxisSensorAccelerationBias
|
||||
Common::Vec3<f32> console_six_axis_sensor_acceleration_bias;
|
||||
Common::Vec<f32, 3> console_six_axis_sensor_acceleration_bias;
|
||||
// nn::settings::system::ConsoleSixAxisSensorAngularVelocityBias
|
||||
Common::Vec3<f32> console_six_axis_sensor_angular_velocity_bias;
|
||||
Common::Vec<f32, 3> console_six_axis_sensor_angular_velocity_bias;
|
||||
// nn::settings::system::ConsoleSixAxisSensorAccelerationGain
|
||||
std::array<u8, 0x24> console_six_axis_sensor_acceleration_gain;
|
||||
// nn::settings::system::ConsoleSixAxisSensorAngularVelocityGain
|
||||
std::array<u8, 0x24> console_six_axis_sensor_angular_velocity_gain;
|
||||
// nn::settings::system::ConsoleSixAxisSensorAngularVelocityTimeBias
|
||||
Common::Vec3<f32> console_six_axis_sensor_angular_velocity_time_bias;
|
||||
Common::Vec<f32, 3> console_six_axis_sensor_angular_velocity_time_bias;
|
||||
// nn::settings::system::ConsoleSixAxisSensorAngularAcceleration
|
||||
std::array<u8, 0x24> console_six_axis_sensor_angular_velocity_acceleration;
|
||||
INSERT_PADDING_BYTES(0x70); // Reserved
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/hle/service/spl/csrng.h"
|
||||
|
||||
namespace Service::SPL {
|
||||
|
||||
CSRNG::CSRNG(Core::System& system_, std::shared_ptr<Module> module_)
|
||||
: Interface(system_, std::move(module_), "csrng") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &CSRNG::GenerateRandomBytes, "GenerateRandomBytes"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
CSRNG::~CSRNG() = default;
|
||||
|
||||
} // namespace Service::SPL
|
||||
@@ -1,20 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/spl/spl_module.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Service::SPL {
|
||||
|
||||
class CSRNG final : public Module::Interface {
|
||||
public:
|
||||
explicit CSRNG(Core::System& system_, std::shared_ptr<Module> module_);
|
||||
~CSRNG() override;
|
||||
};
|
||||
|
||||
} // namespace Service::SPL
|
||||
@@ -13,7 +13,6 @@
|
||||
#include "core/hle/api_version.h"
|
||||
#include "core/hle/service/ipc_helpers.h"
|
||||
#include "core/hle/service/server_manager.h"
|
||||
#include "core/hle/service/spl/csrng.h"
|
||||
#include "core/hle/service/spl/spl.h"
|
||||
#include "core/hle/service/spl/spl_module.h"
|
||||
|
||||
@@ -203,6 +202,18 @@ Result Module::Interface::GetConfigImpl(u64* out_config, ConfigItem config_item)
|
||||
}
|
||||
}
|
||||
|
||||
class CSRNG final : public Module::Interface {
|
||||
public:
|
||||
explicit CSRNG(Core::System& system_, std::shared_ptr<Module> module_)
|
||||
: Interface(system_, std::move(module_), "csrng") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &CSRNG::GenerateRandomBytes, "GenerateRandomBytes"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
~CSRNG() override = default;
|
||||
};
|
||||
|
||||
void LoopProcess(Core::System& system) {
|
||||
auto server_manager = std::make_unique<ServerManager>(system);
|
||||
auto module = std::make_shared<Module>();
|
||||
|
||||
@@ -1,137 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <memory>
|
||||
#include "core/hle/result.h"
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
#include "core/hle/service/cmif_types.h"
|
||||
#include "core/hle/service/ipc_helpers.h"
|
||||
#include "core/hle/service/ulsf/ulsf.h"
|
||||
#include "core/hle/service/server_manager.h"
|
||||
|
||||
namespace Service::ULSF {
|
||||
|
||||
ULSF_U::ULSF_U(Core::System& system_) : ServiceFramework{system_, "ulsf:u"} {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &ULSF_U::GetVersion, "GetVersion"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
ULSF_U::~ULSF_U() = default;
|
||||
|
||||
// Result ULSF_U::GetVersion(Out<ULauncherVersion> out_version) {
|
||||
// LOG_WARNING(Service_SM, "(stubbed)");
|
||||
// *out_version = {};
|
||||
// R_SUCCEED();
|
||||
// }
|
||||
|
||||
void ULSF_U::GetVersion(HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_SM, "(stubbed)");
|
||||
ULauncherVersion version{1, 6, 7};
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushRaw(version);
|
||||
}
|
||||
|
||||
enum class MenuMessage : u32 {
|
||||
Invalid,
|
||||
HomeRequest,
|
||||
SdCardEjected,
|
||||
GameCardMountFailure,
|
||||
PreviousLaunchFailure,
|
||||
ChosenHomebrew,
|
||||
FinishedSleep,
|
||||
ApplicationRecordsChanged,
|
||||
ApplicationVerifyProgress,
|
||||
ApplicationVerifyResult
|
||||
};
|
||||
struct MenuMessageContext {
|
||||
MenuMessage msg;
|
||||
union {
|
||||
struct {
|
||||
Result mount_rc;
|
||||
} gc_mount_failure;
|
||||
struct {
|
||||
char nro_path[0x301];
|
||||
} chosen_hb;
|
||||
struct {
|
||||
bool records_added_or_deleted;
|
||||
} app_records_changed;
|
||||
struct {
|
||||
u64 app_id;
|
||||
u64 done;
|
||||
u64 total;
|
||||
} app_verify_progress;
|
||||
struct {
|
||||
u64 app_id;
|
||||
Result rc;
|
||||
Result detail_rc;
|
||||
} app_verify_rc;
|
||||
};
|
||||
};
|
||||
|
||||
class ULSF_P final : public ServiceFramework<ULSF_P> {
|
||||
public:
|
||||
explicit ULSF_P(Core::System& system_) : ServiceFramework{system_, "ulsf:p"} {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, C<&ULSF_P::Initialize>, "Initialize"},
|
||||
{1, C<&ULSF_P::TryPopMessageContext>, "TryPopMessageContext"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
~ULSF_P() = default;
|
||||
|
||||
Result Initialize(u64 pid) {
|
||||
LOG_WARNING(Service_SM, "(stubbed) pid={}", pid);
|
||||
R_SUCCEED();
|
||||
}
|
||||
Result TryPopMessageContext(OutLargeData<MenuMessageContext, BufferAttr_HipcMapAlias> out_menu_message) {
|
||||
//LOG_WARNING(Service_SM, "(stubbed)");
|
||||
// *out_menu_message = {};
|
||||
// R_SUCCEED();
|
||||
R_THROW(Kernel::ResultInvalidAddress);
|
||||
}
|
||||
};
|
||||
|
||||
class AVM final : public ServiceFramework<AVM> {
|
||||
public:
|
||||
explicit AVM(Core::System& system_): ServiceFramework{system_, "avm"} {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &AVM::Cmd0, "Cmd0"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
~AVM() = default;
|
||||
void Cmd0(HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_SM, "(stubbed)");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
};
|
||||
class WLAN final : public ServiceFramework<WLAN> {
|
||||
public:
|
||||
explicit WLAN(Core::System& system_): ServiceFramework{system_, "wlan"} {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &WLAN::Cmd0, "Cmd0"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
~WLAN() = default;
|
||||
void Cmd0(HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_SM, "(stubbed)");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
};
|
||||
|
||||
void LoopProcess(Core::System& system) {
|
||||
auto server_manager = std::make_unique<ServerManager>(system);
|
||||
server_manager->RegisterNamedService("ulsf:u", std::make_shared<ULSF_U>(system));
|
||||
server_manager->RegisterNamedService("ulsf:p", std::make_shared<ULSF_P>(system));
|
||||
|
||||
server_manager->RegisterNamedService("avm", std::make_shared<AVM>(system));
|
||||
server_manager->RegisterNamedService("wlan", std::make_shared<WLAN>(system));
|
||||
ServerManager::RunServer(std::move(server_manager));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/service/cmif_types.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Service::ULSF {
|
||||
|
||||
struct ULauncherVersion {
|
||||
u8 major;
|
||||
u8 minor;
|
||||
u8 micro;
|
||||
};
|
||||
|
||||
class ULSF_U final : public ServiceFramework<ULSF_U> {
|
||||
public:
|
||||
explicit ULSF_U(Core::System& system_);
|
||||
~ULSF_U();
|
||||
//Result GetVersion(Out<ULauncherVersion> out_version);
|
||||
void GetVersion(HLERequestContext& ctx);
|
||||
};
|
||||
|
||||
void LoopProcess(Core::System& system);
|
||||
|
||||
}
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "core/hle/service/vi/manager_display_service.h"
|
||||
#include "core/hle/service/vi/system_display_service.h"
|
||||
#include "core/hle/service/vi/vi_results.h"
|
||||
#include "service_creator.h"
|
||||
|
||||
namespace Service::VI {
|
||||
|
||||
@@ -38,6 +39,7 @@ IApplicationDisplayService::IApplicationDisplayService(Core::System& system_,
|
||||
{2031, C<&IApplicationDisplayService::DestroyStrayLayer>, "DestroyStrayLayer"},
|
||||
{2101, C<&IApplicationDisplayService::SetLayerScalingMode>, "SetLayerScalingMode"},
|
||||
{2102, C<&IApplicationDisplayService::ConvertScalingMode>, "ConvertScalingMode"},
|
||||
{2103, C<&IApplicationDisplayService::Cmd2103>, "Cmd2103"},
|
||||
{2450, C<&IApplicationDisplayService::GetIndirectLayerImageMap>, "GetIndirectLayerImageMap"},
|
||||
{2451, nullptr, "GetIndirectLayerImageCropMap"},
|
||||
{2460, C<&IApplicationDisplayService::GetIndirectLayerImageRequiredMemoryInfo>, "GetIndirectLayerImageRequiredMemoryInfo"},
|
||||
@@ -289,6 +291,11 @@ Result IApplicationDisplayService::ConvertScalingMode(Out<ConvertedScaleMode> ou
|
||||
}
|
||||
}
|
||||
|
||||
Result IApplicationDisplayService::Cmd2103(Out<std::array<u8, 0x18>> out_unk18) {
|
||||
LOG_WARNING(Service_VI, "(stubbed)");
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IApplicationDisplayService::GetIndirectLayerImageMap(
|
||||
Out<u64> out_size, Out<u64> out_stride,
|
||||
OutBuffer<BufferAttr_HipcMapTransferAllowsNonSecure | BufferAttr_HipcMapAlias> out_buffer,
|
||||
|
||||
@@ -64,6 +64,7 @@ public:
|
||||
Result GetDisplayVsyncEvent(OutCopyHandle<Kernel::KReadableEvent> out_vsync_event,
|
||||
u64 display_id);
|
||||
Result ConvertScalingMode(Out<ConvertedScaleMode> out_scaling_mode, NintendoScaleMode mode);
|
||||
Result Cmd2103(Out<std::array<u8, 0x18>> out_unk18);
|
||||
Result GetIndirectLayerImageMap(
|
||||
Out<u64> out_size, Out<u64> out_stride,
|
||||
OutBuffer<BufferAttr_HipcMapTransferAllowsNonSecure | BufferAttr_HipcMapAlias> out_buffer,
|
||||
|
||||
@@ -300,28 +300,6 @@ Result SharedBufferManager::CreateSession(Kernel::KProcess* owner_process, u64*
|
||||
}
|
||||
}
|
||||
|
||||
// Claim a presentation slot range.
|
||||
u32 slot_base = 0;
|
||||
|
||||
std::array<bool, SharedBufferMaxSessions> in_use{};
|
||||
for (const auto& [existing_aruid, existing] : m_sessions) {
|
||||
const u32 index = existing.presentation_slot_base / SharedBufferSlotsPerSession;
|
||||
|
||||
if (index < in_use.size())
|
||||
in_use[index] = true;
|
||||
}
|
||||
|
||||
u32 index = 0;
|
||||
while (index < in_use.size() && in_use[index])
|
||||
index++;
|
||||
|
||||
if (index >= in_use.size()) {
|
||||
LOG_ERROR(Service_VI, "Out of shared buffer presentation slots ({} sessions)", SharedBufferMaxSessions);
|
||||
R_THROW(VI::ResultOperationFailed);
|
||||
}
|
||||
|
||||
slot_base = index * SharedBufferSlotsPerSession;
|
||||
|
||||
// Map into process.
|
||||
Common::ProcessAddress map_address{};
|
||||
R_TRY(MapSharedBufferIntoProcessAddressSpace(std::addressof(map_address), m_buffer_page_group,
|
||||
@@ -330,7 +308,6 @@ Result SharedBufferManager::CreateSession(Kernel::KProcess* owner_process, u64*
|
||||
// Create new session.
|
||||
auto [it, was_emplaced] = m_sessions.emplace(aruid, SharedBufferSession{});
|
||||
auto& session = it->second;
|
||||
session.presentation_slot_base = slot_base;
|
||||
|
||||
auto& container = m_nvdrv->GetContainer();
|
||||
session.session_id = container.OpenSession(owner_process);
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
#include "core/file_sys/content_archive.h"
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
#include "core/file_sys/patch_manager.h"
|
||||
#include "core/file_sys/program_metadata.h"
|
||||
#include "core/file_sys/romfs_factory.h"
|
||||
#include "core/hle/kernel/k_page_table.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
@@ -208,7 +207,7 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
|
||||
|
||||
const bool should_pass_arguments = std::strcmp(module, "rtld") == 0;
|
||||
const auto tentative_next_load_addr = AppLoader_NSO::LoadModule(
|
||||
process, system, *module_file, code_size, should_pass_arguments, false, nullptr, metadata, {},
|
||||
process, system, *module_file, code_size, should_pass_arguments, false, {},
|
||||
patch_ctx.GetPatchers(), patch_ctx.GetLastIndex());
|
||||
if (!tentative_next_load_addr) {
|
||||
return {ResultStatus::ErrorLoadingNSO, {}};
|
||||
@@ -255,9 +254,8 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
|
||||
|
||||
const VAddr load_addr{next_load_addr};
|
||||
const bool should_pass_arguments = std::strcmp(module, "rtld") == 0;
|
||||
FileSys::ProgramMetadata tmp_metadata{};
|
||||
const auto tentative_next_load_addr = AppLoader_NSO::LoadModule(
|
||||
process, system, *module_file, load_addr, should_pass_arguments, true, nullptr, metadata, pm,
|
||||
process, system, *module_file, load_addr, should_pass_arguments, true, pm,
|
||||
patch_ctx.GetPatchers(), patch_ctx.GetIndex(i));
|
||||
if (!tentative_next_load_addr) {
|
||||
return {ResultStatus::ErrorLoadingNSO, {}};
|
||||
|
||||
+8
-50
@@ -17,14 +17,10 @@
|
||||
#include "common/swap.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/patch_manager.h"
|
||||
#include "core/file_sys/romfs_factory.h"
|
||||
#include "core/file_sys/vfs/vfs_types.h"
|
||||
#include "core/hle/kernel/code_set.h"
|
||||
#include "core/hle/kernel/k_page_table.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/service/filesystem/filesystem.h"
|
||||
#include "core/loader/loader.h"
|
||||
#include "core/loader/nso.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
@@ -70,7 +66,7 @@ FileType AppLoader_NSO::IdentifyType(const FileSys::VirtualFile& in_file) {
|
||||
return FileType::NSO;
|
||||
}
|
||||
|
||||
std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core::System& system, const FileSys::VfsFile& nso_file, const VAddr load_base, const bool should_pass_arguments, const bool load_into_process, VAddr* out_load_base, FileSys::ProgramMetadata metadata, std::optional<FileSys::PatchManager> pm, std::vector<Core::NCE::Patcher>* patches, s32 patch_index) {
|
||||
std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core::System& system, const FileSys::VfsFile& nso_file, VAddr load_base, bool should_pass_arguments, bool load_into_process, std::optional<FileSys::PatchManager> pm, std::vector<Core::NCE::Patcher>* patches, s32 patch_index) {
|
||||
if (nso_file.GetSize() < sizeof(NSOHeader))
|
||||
return std::nullopt;
|
||||
NSOHeader nso_header{};
|
||||
@@ -223,19 +219,9 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core::
|
||||
}
|
||||
}
|
||||
|
||||
const bool is_hbl = true;
|
||||
if (process
|
||||
.LoadFromMetadata(metadata, image_size, 0, 0, is_hbl)
|
||||
.IsError()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto const new_load_base = process.GetEntryPoint().GetValue();
|
||||
if (out_load_base)
|
||||
*out_load_base = new_load_base; // no change
|
||||
// Load codeset for current process
|
||||
process.LoadModule(system.Kernel(), std::move(codeset), new_load_base);
|
||||
return new_load_base + image_size;
|
||||
process.LoadModule(system.Kernel(), std::move(codeset), load_base);
|
||||
return load_base + image_size;
|
||||
}
|
||||
|
||||
AppLoader_NSO::LoadResult AppLoader_NSO::Load(Kernel::KProcess& process, Core::System& system) {
|
||||
@@ -243,34 +229,20 @@ AppLoader_NSO::LoadResult AppLoader_NSO::Load(Kernel::KProcess& process, Core::S
|
||||
return {ResultStatus::ErrorAlreadyLoaded, {}};
|
||||
}
|
||||
|
||||
FileSys::VirtualFile npdm_file{};
|
||||
metadata = FileSys::ProgramMetadata::GetDefault();
|
||||
if (auto const dir = file->GetContainingDirectory()) {
|
||||
npdm_file = dir->GetFile("main.npdm");
|
||||
if (npdm_file) {
|
||||
metadata.Load(npdm_file);
|
||||
}
|
||||
}
|
||||
|
||||
modules.clear();
|
||||
|
||||
// Load module
|
||||
VAddr base_address = GetInteger(process.GetEntryPoint());
|
||||
if (!LoadModule(process, system, *file, base_address, true, true, &base_address, metadata)) {
|
||||
const VAddr base_address = GetInteger(process.GetEntryPoint());
|
||||
if (!LoadModule(process, system, *file, base_address, true, true)) {
|
||||
return {ResultStatus::ErrorLoadingNSO, {}};
|
||||
}
|
||||
|
||||
modules.insert_or_assign(base_address, file->GetName());
|
||||
LOG_DEBUG(Loader, "loaded module {} @ {:#x}", file->GetName(), base_address);
|
||||
|
||||
if (npdm_file) {
|
||||
LOG_WARNING(Loader, "creating associated rom-fs factories for likely standalone NSO");
|
||||
u64 program_id{};
|
||||
ReadProgramId(program_id);
|
||||
system.GetFileSystemController().RegisterProcess(process.GetProcessId(), program_id, std::make_unique<FileSys::RomFSFactory>(*this, system.GetContentProvider(), system.GetFileSystemController()));
|
||||
}
|
||||
|
||||
is_loaded = true;
|
||||
return {ResultStatus::Success, LoadParameters{Kernel::KThread::DefaultThreadPriority, Core::Memory::DEFAULT_STACK_SIZE}};
|
||||
return {ResultStatus::Success, LoadParameters{Kernel::KThread::DefaultThreadPriority,
|
||||
Core::Memory::DEFAULT_STACK_SIZE}};
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NSO::ReadNSOModules(Modules& out_modules) {
|
||||
@@ -278,18 +250,4 @@ ResultStatus AppLoader_NSO::ReadNSOModules(Modules& out_modules) {
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NSO::ReadProgramId(u64& out_program_id) {
|
||||
if (metadata.GetTitleID() == 0)
|
||||
return ResultStatus::ErrorNoControl;
|
||||
out_program_id = metadata.GetTitleID();
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NSO::ReadTitle(std::string& out_title) {
|
||||
auto const raw_name = metadata.GetName();
|
||||
out_title.resize(raw_name.size());
|
||||
std::memcpy(out_title.data(), raw_name.data(), raw_name.size());
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
} // namespace Loader
|
||||
|
||||
+5
-11
@@ -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
|
||||
|
||||
@@ -12,7 +9,6 @@
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/file_sys/patch_manager.h"
|
||||
#include "core/file_sys/program_metadata.h"
|
||||
#include "core/loader/loader.h"
|
||||
|
||||
namespace Core {
|
||||
@@ -94,20 +90,18 @@ public:
|
||||
}
|
||||
|
||||
static std::optional<VAddr> LoadModule(Kernel::KProcess& process, Core::System& system,
|
||||
const FileSys::VfsFile& nso_file, const VAddr load_base,
|
||||
const bool should_pass_arguments, const bool load_into_process, VAddr* out_load_base,
|
||||
FileSys::ProgramMetadata metadata,
|
||||
std::optional<FileSys::PatchManager> pm = {}, std::vector<Core::NCE::Patcher>* patches = nullptr, s32 patch_index = -1);
|
||||
const FileSys::VfsFile& nso_file, VAddr load_base,
|
||||
bool should_pass_arguments, bool load_into_process,
|
||||
std::optional<FileSys::PatchManager> pm = {},
|
||||
std::vector<Core::NCE::Patcher>* patches = nullptr,
|
||||
s32 patch_index = -1);
|
||||
|
||||
LoadResult Load(Kernel::KProcess& process, Core::System& system) override;
|
||||
|
||||
ResultStatus ReadNSOModules(Modules& out_modules) override;
|
||||
ResultStatus ReadProgramId(u64& out_program_id) override;
|
||||
ResultStatus ReadTitle(std::string& title) override;
|
||||
|
||||
private:
|
||||
Modules modules;
|
||||
FileSys::ProgramMetadata metadata;
|
||||
};
|
||||
|
||||
} // namespace Loader
|
||||
|
||||
+42
-39
@@ -101,8 +101,10 @@ struct Memory::Impl {
|
||||
}
|
||||
|
||||
u64 protect_bytes = 0, protect_begin = 0;
|
||||
|
||||
current_page_table->entries.CommitRegion(vaddr >> YUZU_PAGEBITS, (vaddr + size) >> YUZU_PAGEBITS);
|
||||
for (u64 addr = vaddr; addr < vaddr + size; addr += YUZU_PAGESIZE) {
|
||||
const Common::PageType page_type = current_page_table->entries[addr >> YUZU_PAGEBITS].ptr.Type();
|
||||
const Common::PageType page_type = current_page_table->entries.GetUnchecked(addr >> YUZU_PAGEBITS).Type();
|
||||
switch (page_type) {
|
||||
case Common::PageType::RasterizerCachedMemory:
|
||||
if (protect_bytes > 0) {
|
||||
@@ -123,16 +125,14 @@ struct Memory::Impl {
|
||||
}
|
||||
|
||||
[[nodiscard]] u8* GetPointerFromRasterizerCachedMemory(u64 vaddr) const {
|
||||
Common::PhysicalAddress const paddr = current_page_table->entries[vaddr >> YUZU_PAGEBITS].addr;
|
||||
if (paddr)
|
||||
return system.DeviceMemory().GetPointer<u8>(paddr + vaddr);
|
||||
if (u64 paddr = current_page_table->entries[vaddr >> YUZU_PAGEBITS].Pointer(true); paddr)
|
||||
return reinterpret_cast<u8*>(paddr) + vaddr;
|
||||
return {};
|
||||
}
|
||||
|
||||
[[nodiscard]] u8* GetPointerFromDebugMemory(u64 vaddr) const {
|
||||
const Common::PhysicalAddress paddr = current_page_table->entries[vaddr >> YUZU_PAGEBITS].addr;
|
||||
if (paddr != 0)
|
||||
return system.DeviceMemory().GetPointer<u8>(paddr + vaddr);
|
||||
if (u64 paddr = current_page_table->entries[vaddr >> YUZU_PAGEBITS].Pointer(true); paddr)
|
||||
return reinterpret_cast<u8*>(paddr) + vaddr;
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -243,10 +243,12 @@ struct Memory::Impl {
|
||||
std::size_t page_index = addr >> YUZU_PAGEBITS;
|
||||
std::size_t page_offset = addr & YUZU_PAGEMASK;
|
||||
bool user_accessible = true;
|
||||
|
||||
current_page_table->entries.CommitRegion(page_index, page_index + (size >> YUZU_PAGEBITS) + 1);
|
||||
while (remaining_size != 0) {
|
||||
const std::size_t copy_amount = (std::min)(std::size_t(YUZU_PAGESIZE) - page_offset, remaining_size);
|
||||
const auto current_vaddr = u64((page_index << YUZU_PAGEBITS) + page_offset);
|
||||
const auto [pointer, type] = current_page_table->entries[page_index].ptr.PointerType();
|
||||
const auto [pointer, type, _] = current_page_table->entries.GetUnchecked(page_index).PointerTypeBlock();
|
||||
switch (type) {
|
||||
case Common::PageType::Unmapped: {
|
||||
user_accessible = false;
|
||||
@@ -297,10 +299,10 @@ struct Memory::Impl {
|
||||
}
|
||||
|
||||
[[nodiscard]] inline const u8* GetSpan(const VAddr addr, const std::size_t size) const noexcept {
|
||||
return (current_page_table->entries[addr >> YUZU_PAGEBITS].block == current_page_table->entries[(addr + size) >> YUZU_PAGEBITS].block) ? GetPointerSilent(addr) : nullptr;
|
||||
return (current_page_table->entries[addr >> YUZU_PAGEBITS].Block() == current_page_table->entries[(addr + size) >> YUZU_PAGEBITS].Block()) ? GetPointerSilent(addr) : nullptr;
|
||||
}
|
||||
[[nodiscard]] inline u8* GetSpan(const VAddr addr, const std::size_t size) noexcept {
|
||||
return (current_page_table->entries[addr >> YUZU_PAGEBITS].block == current_page_table->entries[(addr + size) >> YUZU_PAGEBITS].block) ? GetPointerSilent(addr) : nullptr;
|
||||
return (current_page_table->entries[addr >> YUZU_PAGEBITS].Block() == current_page_table->entries[(addr + size) >> YUZU_PAGEBITS].Block()) ? GetPointerSilent(addr) : nullptr;
|
||||
}
|
||||
|
||||
bool WriteBlockImpl(const Common::ProcessAddress addr, const void* buffer, const std::size_t size, bool unsafe) {
|
||||
@@ -404,11 +406,14 @@ struct Memory::Impl {
|
||||
// The region is at a granularity of CPU pages.
|
||||
|
||||
const u64 num_pages = ((vaddr + size - 1) >> YUZU_PAGEBITS) - (vaddr >> YUZU_PAGEBITS) + 1;
|
||||
|
||||
current_page_table->entries.CommitRegion(vaddr >> YUZU_PAGEBITS, (vaddr >> YUZU_PAGEBITS) + num_pages);
|
||||
for (u64 i = 0; i < num_pages; ++i, vaddr += YUZU_PAGESIZE) {
|
||||
const Common::PageType page_type = current_page_table->entries[vaddr >> YUZU_PAGEBITS].ptr.Type();
|
||||
auto& entry = current_page_table->entries.GetUnchecked(vaddr >> YUZU_PAGEBITS);
|
||||
const auto [pointer, type, block] = entry.PointerTypeBlock(true);
|
||||
if (debug) {
|
||||
// Switch page type to debug if now debug
|
||||
switch (page_type) {
|
||||
switch (type) {
|
||||
case Common::PageType::Unmapped:
|
||||
ASSERT(false && "Attempted to mark unmapped pages as debug");
|
||||
break;
|
||||
@@ -417,14 +422,14 @@ struct Memory::Impl {
|
||||
// Page is already marked.
|
||||
break;
|
||||
case Common::PageType::Memory:
|
||||
current_page_table->entries[vaddr >> YUZU_PAGEBITS].ptr.Store(0, Common::PageType::DebugMemory);
|
||||
entry.MarkDebug(pointer, block);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
} else {
|
||||
// Switch page type to non-debug if now non-debug
|
||||
switch (page_type) {
|
||||
switch (type) {
|
||||
case Common::PageType::Unmapped:
|
||||
ASSERT(false && "Attempted to mark unmapped pages as non-debug");
|
||||
break;
|
||||
@@ -433,8 +438,7 @@ struct Memory::Impl {
|
||||
// Don't mess with already non-debug or rasterizer memory.
|
||||
break;
|
||||
case Common::PageType::DebugMemory: {
|
||||
u8* const pointer = GetPointerFromDebugMemory(vaddr & ~YUZU_PAGEMASK);
|
||||
current_page_table->entries[vaddr >> YUZU_PAGEBITS].ptr.Store(uintptr_t(pointer) - (vaddr & ~YUZU_PAGEMASK), Common::PageType::Memory);
|
||||
entry.Store(false, Common::PageType::Memory, block, pointer);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -466,8 +470,10 @@ struct Memory::Impl {
|
||||
// is different). This assumes the specified GPU address region is contiguous as well.
|
||||
|
||||
const u64 num_pages = ((vaddr + size - 1) >> YUZU_PAGEBITS) - (vaddr >> YUZU_PAGEBITS) + 1;
|
||||
current_page_table->entries.CommitRegion(vaddr >> YUZU_PAGEBITS, (vaddr >> YUZU_PAGEBITS) + num_pages);
|
||||
for (u64 i = 0; i < num_pages; ++i, vaddr += YUZU_PAGESIZE) {
|
||||
const Common::PageType page_type= current_page_table->entries[vaddr >> YUZU_PAGEBITS].ptr.Type();
|
||||
auto& entry = current_page_table->entries.GetUnchecked(vaddr >> YUZU_PAGEBITS);
|
||||
const Common::PageType page_type = entry.Type();
|
||||
if (cached) {
|
||||
// Switch page type to cached if now cached
|
||||
switch (page_type) {
|
||||
@@ -477,7 +483,7 @@ struct Memory::Impl {
|
||||
break;
|
||||
case Common::PageType::DebugMemory:
|
||||
case Common::PageType::Memory:
|
||||
current_page_table->entries[vaddr >> YUZU_PAGEBITS].ptr.Store(0, Common::PageType::RasterizerCachedMemory);
|
||||
entry.MarkRasterizerCached();
|
||||
break;
|
||||
case Common::PageType::RasterizerCachedMemory:
|
||||
// There can be more than one GPU region mapped per CPU region, so it's common
|
||||
@@ -499,13 +505,13 @@ struct Memory::Impl {
|
||||
// that this area is already unmarked as cached.
|
||||
break;
|
||||
case Common::PageType::RasterizerCachedMemory: {
|
||||
if (u8* const pointer = GetPointerFromRasterizerCachedMemory(vaddr & ~YUZU_PAGEMASK); pointer == nullptr) {
|
||||
if (auto [ptr, _, block] = entry.PointerTypeBlock(true); ptr == 0) {
|
||||
// It's possible that this function has been called while updating the
|
||||
// pagetable after unmapping a VMA. In that case the underlying VMA will no
|
||||
// longer exist, and we should just leave the pagetable entry blank.
|
||||
current_page_table->entries[vaddr >> YUZU_PAGEBITS].ptr.Store(0, Common::PageType::Unmapped);
|
||||
entry.Store(false, Common::PageType::Unmapped, block, 0);
|
||||
} else {
|
||||
current_page_table->entries[vaddr >> YUZU_PAGEBITS].ptr.Store(uintptr_t(pointer) - (vaddr & ~YUZU_PAGEMASK), Common::PageType::Memory);
|
||||
entry.Store(false, Common::PageType::Memory, block, ptr);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -539,22 +545,18 @@ struct Memory::Impl {
|
||||
ASSERT_MSG(type != Common::PageType::Memory,
|
||||
"Mapping memory page without a pointer @ {:016x}", base * YUZU_PAGESIZE);
|
||||
|
||||
while (base != end) {
|
||||
page_table.entries[base].ptr.Store(0, type);
|
||||
page_table.entries[base].addr = 0;
|
||||
page_table.entries[base].block = 0;
|
||||
base += 1;
|
||||
}
|
||||
page_table.entries.ZeroRegion(base, end);
|
||||
} else {
|
||||
auto orig_base = base;
|
||||
while (base != end) {
|
||||
auto host_ptr = uintptr_t(system.DeviceMemory().GetPointer<u8>(target)) - (base << YUZU_PAGEBITS);
|
||||
auto backing = GetInteger(target) - (base << YUZU_PAGEBITS);
|
||||
page_table.entries[base].ptr.Store(host_ptr, type);
|
||||
page_table.entries[base].addr = backing;
|
||||
page_table.entries[base].block = orig_base << YUZU_PAGEBITS;
|
||||
auto current_block = block_count.fetch_add(1, std::memory_order_relaxed);
|
||||
ASSERT(current_block != 65535);
|
||||
|
||||
ASSERT_MSG(page_table.entries[base].ptr.Pointer(),
|
||||
page_table.entries.CommitRegion(base, end);
|
||||
while (base != end) {
|
||||
auto host_ptr = reinterpret_cast<u64>(system.DeviceMemory().GetPointer<u8>(target)) - (base << YUZU_PAGEBITS);;
|
||||
auto& entry = page_table.entries.GetUnchecked(base);
|
||||
|
||||
entry.Store(false, type, current_block, host_ptr);
|
||||
ASSERT_MSG(page_table.entries[base].Pointer(),
|
||||
"memory mapping base yield a nullptr within the table");
|
||||
|
||||
base += 1;
|
||||
@@ -569,11 +571,11 @@ struct Memory::Impl {
|
||||
vaddr &= 0xffffffffffffULL;
|
||||
if (AddressSpaceContains(*current_page_table, vaddr, 1)) [[likely]] {
|
||||
// Avoid adding any extra logic to this fast-path block
|
||||
const uintptr_t raw_pointer = current_page_table->entries[vaddr >> YUZU_PAGEBITS].ptr.Raw();
|
||||
if (const uintptr_t pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) [[likely]] {
|
||||
const auto raw = current_page_table->entries[vaddr >> YUZU_PAGEBITS].Raw();
|
||||
if (auto pointer = Common::PageTable::PageEntryData::ExtractPointer(raw); pointer) [[likely]] {
|
||||
return reinterpret_cast<u8*>(pointer + vaddr);
|
||||
} else {
|
||||
switch (Common::PageTable::PageInfo::ExtractType(raw_pointer)) {
|
||||
switch (static_cast<Common::PageType>(raw.type)) {
|
||||
case Common::PageType::Memory:
|
||||
ASSERT_MSG(false, "Mapped memory page without a pointer @ {:#016x}", vaddr);
|
||||
return nullptr;
|
||||
@@ -773,6 +775,7 @@ struct Memory::Impl {
|
||||
#else
|
||||
Common::HostMemory* host_buffer{};
|
||||
#endif
|
||||
std::atomic<u16> block_count = 0;
|
||||
};
|
||||
|
||||
Memory::Memory(Core::System& system_) : system{system_} {
|
||||
@@ -811,7 +814,7 @@ bool Memory::IsValidVirtualAddress(const Common::ProcessAddress vaddr) const {
|
||||
if (page >= page_table.entries.size()) {
|
||||
return false;
|
||||
}
|
||||
const auto [pointer, type] = page_table.entries[page].ptr.PointerType();
|
||||
const auto [pointer, type, _] = page_table.entries[page].PointerTypeBlock();
|
||||
return pointer != 0 || type == Common::PageType::RasterizerCachedMemory ||
|
||||
type == Common::PageType::DebugMemory;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -371,8 +371,10 @@ EmitConfig A32AddressSpace::GetEmitConfig() {
|
||||
|
||||
.page_table_pointer = std::bit_cast<u64>(conf.page_table),
|
||||
.page_table_address_space_bits = 32,
|
||||
.page_table_pointer_mask_bits = conf.page_table_pointer_mask_bits,
|
||||
.page_table_pointer_mask = conf.page_table_pointer_mask,
|
||||
.page_table_log2_stride = conf.page_table_log2_stride,
|
||||
.page_table_marked_bit = conf.page_table_marked_bit,
|
||||
.page_table_sign_extension = conf.page_table_sign_extension,
|
||||
.silently_mirror_page_table = true,
|
||||
.absolute_offset_page_table = conf.absolute_offset_page_table,
|
||||
.detect_misaligned_access_via_page_table = conf.detect_misaligned_access_via_page_table,
|
||||
|
||||
@@ -545,8 +545,10 @@ EmitConfig A64AddressSpace::GetEmitConfig() {
|
||||
|
||||
.page_table_pointer = std::bit_cast<u64>(conf.page_table),
|
||||
.page_table_address_space_bits = conf.page_table_address_space_bits,
|
||||
.page_table_pointer_mask_bits = conf.page_table_pointer_mask_bits,
|
||||
.page_table_pointer_mask = conf.page_table_pointer_mask,
|
||||
.page_table_log2_stride = conf.page_table_log2_stride,
|
||||
.page_table_marked_bit = conf.page_table_marked_bit,
|
||||
.page_table_sign_extension = conf.page_table_sign_extension,
|
||||
.silently_mirror_page_table = conf.silently_mirror_page_table,
|
||||
.absolute_offset_page_table = conf.absolute_offset_page_table,
|
||||
.detect_misaligned_access_via_page_table = conf.detect_misaligned_access_via_page_table,
|
||||
|
||||
@@ -128,8 +128,10 @@ struct EmitConfig {
|
||||
// Page table
|
||||
u64 page_table_pointer;
|
||||
std::size_t page_table_address_space_bits;
|
||||
int page_table_pointer_mask_bits;
|
||||
u64 page_table_pointer_mask;
|
||||
std::size_t page_table_log2_stride;
|
||||
std::optional<std::uint8_t> page_table_marked_bit;
|
||||
std::optional<std::uint8_t> page_table_sign_extension;
|
||||
bool silently_mirror_page_table;
|
||||
bool absolute_offset_page_table;
|
||||
u8 detect_misaligned_access_via_page_table;
|
||||
|
||||
@@ -273,9 +273,18 @@ std::pair<oaknut::XReg, oaknut::XReg> InlinePageTableEmitVAddrLookup(oaknut::Cod
|
||||
// load x0 = *<(u8*)pagetable + index>
|
||||
code.LDR(Xscratch0, Xpagetable, Xscratch0);
|
||||
|
||||
if (ctx.conf.page_table_pointer_mask_bits != 0) {
|
||||
const u64 mask = u64(~u64(0)) << ctx.conf.page_table_pointer_mask_bits;
|
||||
code.AND(Xscratch0, Xscratch0, mask);
|
||||
if (ctx.conf.page_table_marked_bit) {
|
||||
// check for marked bit
|
||||
code.TBNZ(Xscratch0, *ctx.conf.page_table_marked_bit, *fallback);
|
||||
}
|
||||
|
||||
if (ctx.conf.page_table_pointer_mask != 0) {
|
||||
code.AND(Xscratch0, Xscratch0, ctx.conf.page_table_pointer_mask);
|
||||
}
|
||||
|
||||
// TODO: combine this with page_table_pointer_mask
|
||||
if (ctx.conf.page_table_sign_extension) {
|
||||
code.SBFM(Xscratch0, Xscratch0, 0, *ctx.conf.page_table_sign_extension);
|
||||
}
|
||||
|
||||
code.CBZ(Xscratch0, *fallback);
|
||||
|
||||
@@ -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>
|
||||
@@ -35,6 +36,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
|
||||
@@ -205,6 +208,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
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user