Compare commits

..

66 Commits

Author SHA1 Message Date
lizzie e52e920b14 fix for tests?!?!? 2026-09-10 00:24:21 +00:00
lizzie d58ab98794 use common return point to not corrupt regs also use jit state for checkbit :) 2026-09-10 00:24:21 +00:00
lizzie b84e63f06b appropriate new terminals 2026-09-10 00:24:21 +00:00
lizzie 2db4c35c54 fix ppc build errs as well 2026-09-10 00:24:21 +00:00
lizzie d3d26797c1 fix ppc 2026-09-10 00:24:21 +00:00
lizzie 99a65383f1 fixup wrong ARCHITECTURE_powerpc64 2026-09-10 00:24:21 +00:00
lizzie 5f47a7f064 fastlink impl 2026-09-10 00:24:21 +00:00
lizzie 9c0d41b55f update to newer dynarmic changes 2026-09-10 00:24:21 +00:00
lizzie 2cc7a94053 update loicense 2026-09-10 00:24:21 +00:00
lizzie b30e68a599 cross compile insn, exclude x11 from headless 2026-09-10 00:24:21 +00:00
lizzie 4e1f277432 emission focused on branch/bctr 2026-09-10 00:24:21 +00:00
lizzie 9b0008008a move docs inline, add data.txt + altivec_data.txt 2026-09-10 00:24:21 +00:00
lizzie e47aee1215 the abi sucks
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-10 00:24:21 +00:00
lizzie 61fd747cbd fix stdu/ldu
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-10 00:24:21 +00:00
lizzie bfcdcdc145 save more regs
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-10 00:24:21 +00:00
lizzie 7a80ce616b Use ctr like gcc does
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-10 00:24:21 +00:00
lizzie ed8d2d9ded more ppc fixes
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-10 00:24:21 +00:00
lizzie 4e5f0cd4e1 chained calls for ppc64
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-10 00:24:21 +00:00
lizzie f8685e0c92 inline a32core/a64core
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-10 00:24:21 +00:00
crueter 2b952b79d7 build fixes + gentoo cross docs
Signed-off-by: crueter <crueter@eden-emu.dev>
2026-09-10 00:24:20 +00:00
lizzie c040f73794 fix invalidations, use stack for checkbit
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-10 00:24:19 +00:00
lizzie 08c284fcb8 A64checkbit
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-10 00:23:47 +00:00
lizzie 16b103aa6c A64: addition fixes extra
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-10 00:23:47 +00:00
lizzie 36bd1c262a even more stupid fixes i feel angry
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-10 00:23:47 +00:00
lizzie 39ee32a311 fix xs stuffs
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-10 00:23:47 +00:00
lizzie 51224713b7 more bclr fixes
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-10 00:23:47 +00:00
lizzie 08e5861af9 fixes for cmpld(i) encodings
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-10 00:23:47 +00:00
lizzie a3ecd2b6cb C -> O for ppc64; impl NZCV?
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-10 00:23:47 +00:00
lizzie 70b4070709 more fixes
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-10 00:23:47 +00:00
lizzie 72bc40c50b i forgot jit pointer, again
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-10 00:23:47 +00:00
lizzie 76b1180d02 just use STD() + offset, reorder stuff
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-10 00:23:47 +00:00
lizzie 23e11d2a58 let type deduction do its thing
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-10 00:23:47 +00:00
lizzie ff4ae20a88 reglock draft 1
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-10 00:23:47 +00:00
lizzie 37d4c6e636 A64: ADD now passes
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-10 00:23:46 +00:00
lizzie a06a5377ad terminal draft1
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-10 00:23:46 +00:00
lizzie 684188d375 "A64: ADD" passes (except on PC check)
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-10 00:23:46 +00:00
lizzie 6e69740ab7 polish up A64 to be ready to accept stuff (but NO-op)
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-10 00:23:46 +00:00
lizzie f6d6e3c195 remove args, directly ref inst stuff
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-10 00:23:46 +00:00
lizzie 695a40872a fix license
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-10 00:23:46 +00:00
lizzie 897f9d5029 exclude powah from non-ppc64
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-10 00:23:46 +00:00
lizzie e79ece6518 generation of add and extraction of flags
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-10 00:23:46 +00:00
lizzie 3d6974607e first emitted block
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-10 00:23:46 +00:00
lizzie d85d576b0d backwards relocs
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-10 00:23:46 +00:00
lizzie e4c55e1aff relocations
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-10 00:23:46 +00:00
lizzie 8c3404960a cross compile instructions; ignore toolchain files
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-10 00:23:46 +00:00
lizzie 0325b44d8e spinlocks, annoyances with organistaion
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-10 00:23:46 +00:00
lizzie a86e24731d cross compile instructions
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-10 00:23:45 +00:00
lizzie aafbcd6ce1 a64+a32 stubs (+some impls)
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-10 00:23:45 +00:00
lizzie 2c57681ca1 fix stuff? 2026-09-10 00:23:43 +00:00
Exverge 5f142c7926 [common/core/dynarmic] Optimize page table allocations (#4219)
Reduces the page entries from 32 bytes to 8 and rewrites `VirtualBuffer` to be more efficient in memory usage and specifically for large zero regions.

The page table will now only reserve 1GiB instead of 4GiB and of this memory it should only use at most ~8MiB.

This PR has the side effect of using Eden on Windows on low memory systems much more plausible since it would previously require ~10GiB of committable memory at front (despite using ~5-6 at most, inadvertently stalling other processes) where as now it should only require around the amount that it'll actually use.

Co-authored-by: Lizzie <lizzie@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4219
Reviewed-by: Maufeat <sahyno1996@gmail.com>
Reviewed-by: lizzie <lizzie@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-09-09 21:35:49 +02:00
lizzie c5f6f1ca5e [common/host_memory] Remove throw on benign path (#4359)
The caller of GetFuncAddress already handles the case gracefully. Fixes crashes when the symbol aint present

Signed-off-by: lizzie <lizzie@eden-emu.dev>

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4359
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: Maufeat <sahyno1996@gmail.com>
2026-09-09 13:18:43 +02:00
lizzie dcbcdf16ad [hle/ns] implement IReadOnlyApplicationControlDataInterface::ListApplicationIcon (#3770)
Signed-off-by: lizzie <lizzie@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3770
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: Maufeat <sahyno1996@gmail.com>
2026-09-09 13:02:02 +02:00
lizzie ed57836903 [ips_layer] remove trailing whitespace to fix hex parsing on TLTD mods (#4336)
Signed-off-by: lizzie <lizzie@eden-emu.dev>

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

...another edge case, this time with
`00297f24 34008052 // 60FPS`
would get parsed as
`34008052 `
which isn't quite correct

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4336
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: Maufeat <sahyno1996@gmail.com>
2026-09-09 13:01:40 +02:00
lizzie 20f9aa4cfe [qt] Update FAQ links (#3710)
Signed-off-by: lizzie <lizzie@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3710
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: Ghost <>
Reviewed-by: crueter <crueter@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-09-09 11:57:40 +02:00
lizzie 6f6a8f6d67 [svc] don't truncate 64-bit SVC args on trace logs (#4374)
Small inconvenience with args

Signed-off-by: lizzie <lizzie@eden-emu.dev>

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4374
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: Maufeat <sahyno1996@gmail.com>
2026-09-09 11:14:22 +02:00
lizzie 2f787ddb8c [dynarmic] Fix broken tests and SIGABRT with f16 inst gen (#4366)
F16 instructions aren't supported yet, thus disable them. Also compilation/build errors.

Signed-off-by: lizzie <lizzie@eden-emu.dev>

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4366
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: Maufeat <sahyno1996@gmail.com>
2026-09-09 11:14:03 +02:00
lizzie 23585820bc [hle] correct IClientProcess::RegisterClient, impl cmd 3100,3102 for ns:vm (#4376)
ldn had wrong signature
ns:vm had missing function stubs that would make some games assert on missing impl

Signed-off-by: lizzie <lizzie@eden-emu.dev>

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4376
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: Maufeat <sahyno1996@gmail.com>
2026-09-09 11:13:42 +02:00
lizzie e5b8b10188 [hle] Fix fsp-pr not being found (#4379)
Should be `fsp-pr`, not `fsp:pr`

Signed-off-by: lizzie <lizzie@eden-emu.dev>

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4379
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: Maufeat <sahyno1996@gmail.com>
2026-09-08 21:17:19 +02:00
lizzie 8a10109e24 [vulkan] Masquerade VkInfo as PUBGMobile + UE (#4320)
Signed-off-by: lizzie <lizzie@eden-emu.dev>

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

PR I was talking about with @CamilleLaVey , please tell me if this is a stupid idea OR has a chance to work
applicationname is a wild guess

Co-authored-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4320
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: Maufeat <sahyno1996@gmail.com>
2026-09-08 21:04:08 +02:00
CamilleLaVey a538cd9aff [shader_recompiler, vulkan] Virtual buffer pages with multi-range + sparse buffers binding (#4362)
Storage buffers can span GPU pages that aren't contiguous in host memory, but the buffer cache assumed one contiguous buffer per binding, so anything crossing a mapping boundary read the wrong bytes. Multi-range binding resolves the real segments and presents them to the shader as one buffer, aliasing their memory into a sparse VkBuffer or gathering them into a copy, all of this was mostly fixed by making a path to use sparse on buffers (the same way as texture cache has it) and retrieve properly the mapping ranges to the virtual pages, including the actual structure on the use of binding sparse.

Meanwhile this fixes Monster Hunter Sunbreak z-fighting, bad performance and mostly vertex explosions (coming from the contiguous buffer read from the shader's game) it's only the basis for a further investigation towards how are we gonna treat phi calculations, optimized them and mostly eradicate the currently excess of exposure on the textures where the lightning trace should not being reflected.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4362
Reviewed-by: lizzie <lizzie@eden-emu.dev>
Reviewed-by: Maufeat <sahyno1996@gmail.com>
2026-09-08 20:42:20 +02:00
lizzie ce202292cf [gpu] Destroy GPU thread after all non-trivial members had been destroyed (#4368)
Should help fix any game hangs after closing the emulator, or sporadic hangs when restarting.

Signed-off-by: lizzie <lizzie@eden-emu.dev>

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4368
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: Maufeat <sahyno1996@gmail.com>
2026-09-08 20:40:19 +02:00
lizzie 87e5d0b6e4 [hle] VirtualBoy classics fixes, QLaunch IPC fixes, implement extra ams SVCInfo (#4343)
Signed-off-by: lizzie <lizzie@eden-emu.dev>

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

`OpenSystemApplicationProxy`
`OpenSystemApplicationProxy`
ALSO yes those cmds DO NOT take an attribute
Implemented SVC infos to match Atmosphere's values
Implement more stub I2C session cmds
stub CMD2103 for rhythm groove
better error handling on svc get info to match ams (see ams svc info)
Virtual Boy classic fix adept from https://git.ryujinx.app/projects/Kenji-NX/commit/2288084b531b185f5840a696b23265014f07e2f2 by @Mythrax
acc:u1 was missing the *Deprecated cmd

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4343
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: Maufeat <sahyno1996@gmail.com>
2026-09-08 20:39:55 +02:00
lizzie fdd8d4252c [common] remove unused vector_math.h fluff (#4333)
Signed-off-by: lizzie <lizzie@eden-emu.dev>

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

We don't use half of this file for anything -- it's dead code
also the NEON intrinsic replacements suck -- we are not beating GLM (plus we use registers for decomposition instead of using vec regs like everyone else does, so the "optimizations" dont matter)

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4333
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: Maufeat <sahyno1996@gmail.com>
2026-09-08 20:39:39 +02:00
PavelBARABANOV 1036982d2f partial revert 54cd5fb8eb (#4355)
- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------
It fixes display issues when the overlay applet is enabled, thanks Bruno.

Fixes freezes when enabling real applet keyboard  in qlaunch.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4355
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: lizzie <lizzie@eden-emu.dev>
Reviewed-by: Maufeat <sahyno1996@gmail.com>
2026-09-08 20:39:08 +02:00
xbzk e27650cf42 [nce] avoid cyrilic utf8 strings being interpreted as exclusive store instructions (#4369)
- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

Problem: In cyrilic idioms, Absolum won't past beyond 1st area (intro miniboss).

From the user panic i`ve extracted these strings:

 > GlobalState.request_next()
 > save_state_to_saveGame(...)
 > platform.ProcessSave()
 > bin_serialize_to_file(...)
 > StandaloneTypeModel.Write(SaveGame, ...)
 > Write(SaveGame.AdventureSaveSlot, ...)
 > Write(PlayerState, ...)
 > ProtoBuf.ProtoWriter.WriteString(...) + 0xcc
 > ProtoBuf.Helpers.DebugAssert(...)
 > Debug.Fail(...)

By debugging ProtoBuf.ProtoWriter.WriteString(...) argument it was found that the string Отлично! (Great! expression for combos between 6 and 30 iirc) was reaching a size comparison of different values and causing an assert in guest side..

It seems the game tries to save highest combo level. I've managed to skip this bug by doing a perfect combo for entire level, so that the problematic word never was generated or attempted to be saved.

After further research, the issue chain was identified: Game maths literal string size via two ways and compare, and one of them was getting replaced by a wrong value due to Eden letting the literal string be interpreted as a real instruction.

 Logic added:
        const bool pair = decltype(l)::ExtractValue(raw) & 1;
        const bool fixed_rt2 = decltype(rt2)::ExtractValue(raw) == 0b11111;
        return pair || fixed_rt2;

   - pair checks bit 21 (L & 1): register-pair instructions actually use Rt2, so they remain accepted.
   - For single-register forms, require Rt2 == 31. The table word fails this check, so NCE leaves it intact.

Sources
https://www.scs.stanford.edu/~zyedidia/arm64/stxrb.html
https://www.scs.stanford.edu/~zyedidia/arm64/stxp.html
https://github.com/qemu/qemu/blob/master/target/arm/tcg/a64.decode#L344

Altough the solution is a case specific guard, it is data agnostic: This is a true fix to avoid a specific case in which a literal may be interpreted as a valid instruction, without interfering with other true instruction cases.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4369
Reviewed-by: lizzie <lizzie@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-09-08 20:10:41 +02:00
xbzk ecb2ae4076 [settings] move program_args and debug_knobs back to debugging category and make them pergameable (#4378)
- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

I have moved debug_knobs and program_args to System category, to make them pergameable without hassle.
But the hassle came: They were automatically readded to Qt's System tab, doubling them.
If it were to face hassle either way, the right thing is to move them back to Debugging category, then make them exceptionally pergameable.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4378
Reviewed-by: lizzie <lizzie@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-09-08 20:05:56 +02:00
171 changed files with 10765 additions and 2230 deletions
+1
View File
@@ -19,6 +19,7 @@ log.txt
# Generated source files # Generated source files
src/common/scm_rev.cpp src/common/scm_rev.cpp
dist/english_plurals/generated_en.ts dist/english_plurals/generated_en.ts
*-toolchain.cmake
# Project/editor files # Project/editor files
*.swp *.swp
+18
View File
@@ -0,0 +1,18 @@
# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
set(CROSS_TARGET "powerpc64le" CACHE STRING "Cross-compilation target (aarch64, powerpc64le, riscv64, etc)")
set(CMAKE_SYSROOT /usr/${CROSS_TARGET}-unknown-linux-gnu)
set(CMAKE_C_COMPILER ${CROSS_TARGET}-unknown-linux-gnu-gcc)
set(CMAKE_CXX_COMPILER ${CROSS_TARGET}-unknown-linux-gnu-g++)
# search programs in the host environment
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
# search headers and libraries in the target environment
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH /usr/${CROSS_TARGET}-unknown-linux-gnu)
+137 -1
View File
@@ -2,7 +2,122 @@
General guide for cross compiling. 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. 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 ```sh
qemu-aarch64 build/aarch64/bin/eden qemu-aarch64 build/aarch64/bin/eden
``` ```
## PowerPC
This is a guide for FreeBSD users mainly.
Now you got a PowerPC sysroot - quickly decompress it somewhere, say `/home/user/opt/powerpc64le`. Create a toolchain file, for example `powerpc64le-toolchain.cmake`; always [consult the manual](https://man.freebsd.org/cgi/man.cgi?query=cmake-toolchains&sektion=7&manpath=FreeBSD+13.2-RELEASE+and+Ports).
There is a script to automatically do all of this under `./tools/setup-cross-sysroot.sh`.
Remember to add `-mabi=elfv1` to `CFLAGS`/`CXXFLAGS` otherwise the program will crash.
Specify:
- `YUZU_USE_CPM`: Set this to `ON` so packages can be found and built if your sysroot doesn't have them.
- `YUZU_USE_EXTERNAL_FFMPEG`: Set this to `ON` as well.
Then run using a program such as QEMU to emulate userland syscalls:
```sh
cmake --build build-ppc64-pc-freebsd -t dynarmic_tests -- -j8 && qemu-ppc64-static -L $HOME/opt/ppc64-freebsd/sysroot ./build-ppc64-pc-freebsd/bin/dynarmic_tests
```
+8
View File
@@ -0,0 +1,8 @@
# PowerPC 64 backend
The ppc64 backend currently only supports the little endian variant, with big endian support being experimental for the time being. Additionally only A32 is supported (for now).
- Flag handling: Flags are emulated via software, while there may be some funny tricks with the CTR, I'd rather not bother - plus it's widely known that those instructions are not nice on real metal - so I would rather take the i-cache cost.
- 128-bit atomics: No 128-bit atomic support is provided, this may cause wrong or erroneous execution in some contexts.
To handle endianess differences all 16/32/64-bit loads and stores to the "emulated memory" are byteswapped beforehand.
+2 -1
View File
@@ -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). 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). See [an example usage](../../src/dynarmic/tests/print_info.cpp).
Design documentation can be found at [./Design.md](./Design.md). Design documentation can be found at [./Design.md](./Design.md).
PowerPC design documentation can be found at [./PowerPC](./PowerPC.md).
Alternatives to Dynarmic Alternatives to Dynarmic
------------------------ ------------------------
+5
View File
@@ -249,6 +249,11 @@ endif()
# TZDB (Time Zone Database) # TZDB (Time Zone Database)
add_subdirectory(nx_tzdb) add_subdirectory(nx_tzdb)
# PowerPC emitter
if (ARCHITECTURE_powerpc64)
add_subdirectory(powah)
endif()
add_library(tz tz/tz/tz.cpp) add_library(tz tz/tz/tz.cpp)
target_include_directories(tz PUBLIC ./tz) target_include_directories(tz PUBLIC ./tz)
+2 -2
View File
@@ -166,7 +166,7 @@ detect_architecture_symbols(
"_M_MRX000") "_M_MRX000")
detect_architecture_symbols( detect_architecture_symbols(
ARCH ppc64 ARCH powerpc64
SYMBOLS SYMBOLS
"__ppc64__" "__ppc64__"
"__powerpc64__" "__powerpc64__"
@@ -174,7 +174,7 @@ detect_architecture_symbols(
"_M_PPC64") "_M_PPC64")
detect_architecture_symbols( detect_architecture_symbols(
ARCH ppc ARCH powerpc
SYMBOLS SYMBOLS
"__ppc__" "__ppc__"
"__ppc" "__ppc"
+10
View File
@@ -0,0 +1,10 @@
# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
add_library(powah INTERFACE)
target_include_directories(powah INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
target_compile_options(powah INTERFACE -Wno-unused-parameter)
add_executable(powah_tests tests.cpp)
create_target_directory_groups(powah_tests)
target_link_libraries(powah_tests PRIVATE Catch2::Catch2WithMain)
+145
View File
@@ -0,0 +1,145 @@
vabsdub,VX,04,1027
vabsduh,VX,04,1091
vabsduw,VX,04,1155
vaddcuw,VX,04,384
vaddfp,VX,04,10
vaddsbs,VX,04,768
vaddshs,VX,04,832
vaddsws,VX,04,896
vaddubm,VX,04,0
vaddubs,VX,04,512
vadduhm,VX,04,64
vadduhs,VX,04,576
vadduwm,VX,04,128
vadduws,VX,04,640
vand,VX,04,1028
vandc,VX,04,1092
vavgsb,VX,04,1282
vavgsh,VX,04,1346
vavgsw,VX,04,1410
vavgub,VX,04,1026
vavguh,VX,04,1090
vavguw,VX,04,1154
vcfsx,VDU,04842
vcfux,VDU,04778
vcmpbfp,VXR,04,966
vcmpeqfp,VXR,04,198
vcmpequb,VXR,04,6
vcmpequh,VXR,04,70
vcmpequw,VXR,04,134
vcmpgefp,VXR,04,454
vcmpgtfp,VXR,04,710
vcmpgtsb,VXR,04,774
vcmpgtsh,VXR,04,838
vcmpgtsw,VXR,04,902
vcmpgtub,VXR,04,518
vcmpgtuh,VXR,04,582
vcmpgtuw,VXR,04,646
vctsxs,VDU,04970
vctuxs,VDU,04906
vexptefp,VY,04,394
vlogefp,VY,04,458
vmaddfp,VXC,46
vmaxfp,VX,04,1034
vmaxsb,VX,04,258
vmaxsh,VX,04,322
vmaxsw,VX,04,386
vmaxub,VX,04,2
vmaxuh,VX,04,66
vmaxuw,VX,04,130
vmhaddshs,VXC,32
vmhraddshs,VXC,33
vminfp,VX,04,1098
vminsb,VX,04,770
vminsh,VX,04,834
vminsw,VX,04,898
vminub,VX,04,514
vminuh,VX,04,578
vminuw,VX,04,642
vmladduhm,VXC,34
vmrghb,VX,04,12
vmrghh,VX,04,76
vmrghw,VX,04,140
vmrglb,VX,04,268
vmrglh,VX,04,332
vmrglw,VX,04,396
vmsummbm,VXC,37
vmsumshm,VXC,40
vmsumshs,VXC,41
vmsumubm,VXC,36
vmsumuhm,VXC,38
vmsumuhs,VXC,39
vmulesb,VX,04,776
vmulesh,VX,04,840
vmuleub,VX,04,520
vmuleuh,VX,04,584
vmulosb,VX,04,264
vmulosh,VX,04,328
vmuloub,VX,04,8
vmulouh,VX,04,72
vnmsubfp,VXC,47
vnor,VX,04,1284
vor,VX,04,1156
vperm,VXC,43
vpkpx,VX,04,782
vpkshss,VX,04,398
vpkshus,VX,04,270
vpkswss,VX,04,462
vpkswus,VX,04,334
vpkuhum,VX,04,14
vpkuhus,VX,04,142
vpkuwum,VX,04,78
vpkuwus,VX,04,206
vrefp,VY,04,266
vrfim,VY,04,714
vrfin,VY,04,522
vrfip,VY,04,650
vrfiz,VY,04,586
vrlb,VX,04,4
vrlh,VX,04,68
vrlw,VX,04,132
vrsqrtefp,VY,04,330
vsel,VXC,42
vsl,VX,04,452
vslb,VX,04,260
vsldoi,VX,04,/ SH 44
vslh,VX,04,324
vslo,VX,04,1036
vslw,VX,04,388
vspltb,VU,04,524
vsplth,VU,04,588
vspltisb,VS,04,780
vspltish,VS,04,844
vspltisw,VS,04,908
vspltw,VSU,04,652
vsr,VX,04,708
vsrab,VX,04,772
vsrah,VX,04,836
vsraw,VX,04,900
vsrb,VX,04,516
vsrh,VX,04,580
vsro,VX,04,1100
vsrw,VX,04,644
vsubcuw,VX,04,1408
vsubfp,VX,04,74
vsubsbs,VX,04,1792
vsubshs,VX,04,1856
vsubsws,VX,04,1920
vsububm,VX,04,1024
vsububs,VX,04,1536
vsubuhm,VX,04,1088
vsubuhs,VX,04,1600
vsubuwm,VX,04,1152
vsubuws,VX,04,1664
vsumsws,VX,04,1928
vsum2sws,VX,04,1672
vsum4sbs,VX,04,1800
vsum4shs,VX,04,1608
vsum4ubs,VX,04,1544
vupkhpx,VY,04,846
vupkhsb,VY,04,526
vupkhsh,VY,04,590
vupklpx,VY,04,974
vupklsb,VY,04,654
vupklsh,VY,04,718
vxor,VX,04,122
Vendored Executable
+6
View File
@@ -0,0 +1,6 @@
#!/bin/sh
# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
$CC data2code.c -o data2code && ./data2code data.txt >powah_gen_base.hpp
$CC --target=powerpc64le test.S -c -o test && objdump -SC test
+222
View File
@@ -0,0 +1,222 @@
add[o][.],Add,XO,31,266
addc[o][.],Add Carrying,XO,31,10
adde[o][.],Add Extended,XO,31,138
addi,Add Immediate,D,14,
addic,Add Immediate Carrying,D,12,
addic.,Add Immediate Carrying and Record,D,13,
addis,Add Immediate Shifted,D,15,
addme[o][.],Add to Minus One Extended,XO,31,234
addze[o][.],Add to Zero Extended,XO,31,202
and[.],AND,X,31,28
andc[.],AND with Complement,X,31,60
andi.,AND Immediate,D,28,
andis.,AND Immediate Shifted,D,29,
b[l][a],Branch,I,18,
bc[l][a],Branch Conditional,B,16,
bcctr[l],Branch Conditional to Count Register,XL,19,528
bclr[l],Branch Conditional Link Register,XL,19,16
cmp,Compare,X,31,0
cmpi,Compare Immediate,D,11,
cmpl,Compare Logical,X,31,32
cmpli,Compare Logical Immediate,D,10,
cntlzd,Count Leading Zeros Doubleword,X,31,58
cntlzw[.],Count Leading Zeros Word,X,31,26
crand,Condition Register AND,XL,19,257
crandc,Condition Register AND with Complement,XL,19,129
creqv,Condition Register Equivalent,XL,19,289
crnand,Condition Register NAND,XL,19,225
crnor,Condition Register NOR,XL,19,33
cror,Condition Register OR,XL,19,449
crorc,Condition Register OR with Complement,XL,19,417
crxor,Condition Register XOR,XL,19,193
dcbf,Data Cache Block Flush,X,31,86
dcbi,Data Cache Block Invalidate,X,31,470
dcbst,Data Cache Block Store,X,31,54
dcbt,Data Cache Block Touch,X,31,278
dcbtst,Data Cache Block Touch for Store,X,31,246
dcbz,Data Cache Block Set to Zero,X,31,1014
divd,Divide Doubleword,XO,31,489
divdu,Divide Doubleword Unsigned,XO,31,457
divw[o][.],Divide Word,XO,31,491
divwu[o][.],Divide Word Unsigned,XO,31,459
eciwx,External Control in Word Indexed (opt.),X,31,310
ecowx,External Control out Word Indexed (opt.),X,31,438
eieio,Enforce In-order Execution of I/O,X,31,854
eqv[.],Equivalent,X,31,284
extsb[.],Extend Sign Byte,X,31,954
extsh[.],Extend Sign Halfword,XO,31,922
extsw,Extend Sign Word,X,31,986
fabs[.],Floating Absolute Value,X,63,264
fadd[.],Floating Add,A,63,21
fadds[.],Floating Add Single,A,59,21
fcfid,Floating Convert from Integer Doubleword,X,63,846
fcmpo,Floating Compare Ordered,X,63,32
fcmpu,Floating Compare Unordered,XL,63,0
fctid,Floating Convert to Integer Doubleword,X,63,814
fctidz,Floating Convert to Integer Doubleword with Round Toward Zero,X,63,815
fctiw[.],Floating Convert to Integer Word,X,63,14
fctiwz[.],Floating Convert to Integer Word with Round to Zero,XL,63,15
fdiv[.],Floating Divide,A,63,18
fdivs[.],Floating Divide Single,A,59,18
fmadd[.],Floating Multiply-Add,A,63,29
fmadds[.],Floating Multiply-Add Single,A,59,29
fmr[.],Floating Move Register,X,63,72
fmsub[.],Floating Multiply-Subtract,A,63,28
fmsubs[.],Floating Multiply-Subtract Single,A,59,28
fmul[.],Floating Multiply,A,63,25
fmuls[.],Floating Multiply Single,A,59,25
fnabs[.],Floating Negative Absolute Value,X,63,136
fneg[.],Floating Negate,X,63,40
fnmadd[.],Floating Negative Multiply-Add,A,63,31
fnmadds[.],Floating Negative Multiply-Add Single,A,59,31
fnmsub[.],Floating Negative Multiply-Subtract,A,63,30
fnmsubs[.],Floating Negative Multiply-Subtract Single,A,59,30
fres[.],Floating Reciprocal Estimate Single (optional),A,59,24
frsp[.],Floating Round to Single Precision,X,63,12
frsqrte[.],Floating Reciprocal Square Root Estimate (optional),A,63,26
fsel[.],Floating-Point Select (optional),A,63,23
fsub[.],Floating Subtract,A,63,20
fsubs[.],Floating Subtract Single,A,59,20
icbi,Instruction Cache Block Invalidate,X,31,982
isync,Instruction Synchronize,X,19,150
lbz,Load Byte and Zero,D,34,
lbzu,Load Byte and Zero with Update,D,35,
lbzux,Load Byte and Zero with Update Indexed,X,31,119
lbzx,Load Byte and Zero Indexed,X,31,87
ld,Load Doubleword,DS,58,0
ldarx,Load Doubleword and Reserve Indexed,X,31,84
ldu,Load Doubleword with Update,DS,58,1
ldux,Load Doubleword with Update Indexed,X,31,53
ldx,Load Doubleword Indexed,X,31,21
lfd,Load Floating-Point Double,D,50,
lfdu,Load Floating-Point Double with Update,D,51,
lfdux,Load Floating-Point Double with Update Indexed,X,31,631
lfdx,Load Floating-Point Double Indexed,X,31,599
lfs,Load Floating-Point Single,D,48,
lfsu,Load Floating-Point Single with Update,D,49,
lfsux,Load Floating-Point Single with Update Indexed,X,31,567
lfsx,Load Floating-Point Single Indexed,X,31,535
lha,Load Half Algebraic,D,42,
lhau,Load Half Algebraic with Update,D,43,
lhaux,Load Half Algebraic with Update Indexed,X,31,375
lhax,Load Half Algebraic Indexed,X,31,343
lhbrx,Load Half Byte-Reversed Indexed,X,31,790
lhz,Load Half and Zero,D,40,
lhzu,Load Half and Zero with Update,D,41,
lhzux,Load Half and Zero with Update Indexed,X,31,331
lhzx,Load Half and Zero Indexed,X,31,279
lmw,Load Multiple Word,D,46,
lswi,Load String Word Immediate,X,31,597
lswx,Load String Word Indexed,X,31,533
lwa,Load Word Algebraic,DS,58,2
lwarx,Load Word and Reserve Indexed,X,31,20
lwaux,Load Word Algebraic with Update Indexed,X,31,373
lwax,Load Word Algebraic Indexed,X,31,341
lwbrx,Load Word Byte-Reversed Indexed,X,31,534
lwz,Load Word and Zero,D,32,
lwzu,Load Word with Zero Update,D,33,
lwzux,Load Word and Zero with Update Indexed,X,31,55
lwzx,Load Word and Zero Indexed,X,31,23
mcrf,Move Condition Register Field,XL,19,0
mcrfs,Move to Condition Register from FPSCR,X,63,64
mcrxr,Move to Condition Register from XER,X,31,512
mfcr,Move from Condition Register,X,31,19
mffs[.],Move from FPSCR,X,63,583
mfmsr,Move from Machine State Register,X,31,83
mfspr,Move from Special-Purpose Register,X,31,339
mfsr,Move from Segment Register,X,31,595
mfsrin,Move from Segment Register Indirect,X,31,659
mtcrf,Move to Condition Register Fields,XFX,31,144
mtfsb0[.],Move to FPSCR Bit 0,X,63,70
mtfsb1[.],Move to FPSCR Bit 1,X,63,38
mtfsf[.],Move to FPSCR Fields,XFL,63,711
mtfsfi[.],Move to FPSCR Field Immediate,X,63,134
mtmsr,Move to Machine State Register,X,31,146
mtspr,Move to Special-Purpose Register,X,31,467
mtsr,Move to Segment Register,X,31,210
mtsrin,Move to Segment Register Indirect,X,31,242
mulhd,Multiply High Doubleword,XO,31,73
mulhdu,Multiply High Doubleword Unsigned,XO,31,9
mulhw[.],Multiply High Word,XO,31,75
mulhwu[.],Multiply High Word Unsigned,XO,31,11
mulld,Multiply Low Doubleword,XO,31,233
mulli,Multiply Low Immediate,D,07,
mullw[o][.],Multiply Low Word,XO,31,235
nand[.],NAND,X,31,476
neg[o][.],Negate,XO,31,104
nor[.],NOR,X,31,124
or[.],OR,X,31,444
orc[.],OR with Complement,X,31,412
ori,OR Immediate,D,24,
oris,OR Immediate Shifted,D,25,
rfi,Return from Interrupt,X,19,50
rldcl,Rotate Left Doubleword then Clear Left,MDS,30,8
rldcr,Rotate Left Doubleword then Clear Right,MDS,30,9
rldic,Rotate Left Doubleword Immediate then Clear,MD,30,2
rldicl,Rotate Left Doubleword Immediate then Clear Left,MD,30,0
rldicr,Rotate Left Doubleword Immediate then Clear Right,MD,30,1
rldimi,Rotate Left Doubleword Immediate then Mask Insert,MD,30,3
rlwimi[.],Rotate Left Word Immediate then Mask Insert,M,20,
rlwinm[.],Rotate Left Word Immediate then AND with Mask,M,21,
rlwnm[.],Rotate Left Word then AND with Mask,M,23,
sc,System Call,SC,17,
si,Subtract Immediate,D,12,
si.,Subtract Immediate and Record,D,13,
slbia,SLB Invalidate All,X,31,498
slbie,SLB Invalidate Entry,X,31,434
sld,Shift Left Doubleword,X,31,27
slw[.],Shift Left Word,X,31,24
srad,Shift Right Algebraic Doubleword,X,31,794
sradi,Shift Right Algebraic Doubleword Immediate,XS,31,413
srd,Shift Right Doubleword,X,31,539
sraw[.],Shift Right Algebraic Word,X,31,792
srawi[.],Shift Right Algebraic Word Immediate,X,31,824
srw[.],Shift Right Word,X,31,536
stb,Store Byte,D,38,
stbu,Store Byte with Update,D,39,
stbux,Store Byte with Update Indexed,X,31,247
stbx,Store Byte Indexed,X,31,215
std,Store Doubleword,DS,62,0
stdcx,Store Doubleword Conditional Indexed,X,31,214
stdu,Store Doubleword with Update,DS,62,1
stdux,Store Doubleword with Update Indexed,X,31,181
stdx,Store Doubleword Indexed,X,31,149
stfd,Store Floating-Point Double,D,54,
stfdu,Store Floating-Point Double with Update,D,55,
stfdux,Store Floating-Point Double with Update Indexed,X,31,759
stfdx,Store Floating-Point Double Indexed,X,31,727
stfiwx,Store Floating-Point as Integer Word Indexed (optional),X,31,983
stfs,Store Floating-Point Single,D,52,
stfsu,Store Floating-Point Single with Update,D,53,
stfsux,Store Floating-Point Single with Update Indexed,X,31,695
stfsx,Store Floating-Point Single Indexed,X,31,663
sth,Store Half,D,44,
sthbrx,Store Half Byte-Reverse Indexed,X,31,918
sthu,Store Half with Update,D,45,
sthux,Store Half with Update Indexed,X,31,439
sthx,Store Half Indexed,X,31,407
stmw,Store Multiple Word,D,47,
stswi,Store String Word Immediate,X,31,725
stswx,Store String Word Indexed,X,31,661
stw,Store,D,36,
stwbrx,Store Word Byte-Reversed Indexed,X,31,662
stwcx.,Store Word Conditional Indexed,X,31,150
stwu,Store Word with Update,D,37,
stwux,Store Word with Update Indexed,X,31,183
stwx,Store Word Indexed,X,31,151
subf[o][.],Subtract from,XO,31,40
subfc[o][.],Subtract from Carrying,XO,31,08
subfe[o][.],Subtract from Extended,XO,31,136
subfic,Subtract from Immediate Carrying,D,08,
subfme[o][.],Subtract from Minus One Extended,XO,31,232
subfze[o][.],Subtract from Zero Extended,XO,31,200
sync,Synchronize,X,31,598
td,Trap Doubleword,X,31,68
tdi,Trap Doubleword Immediate,D,2,
tlbie,Translation Look-aside Buffer Invalidate Entry (optional),X,31,306
tlbsync,Translation Look-aside Buffer Synchronize (optional),X,31,566
tw,Trap Word,X,31,04
twi,Trap Word Immediate,D,03,
xor[.],XOR,X,31,316
xori,XOR Immediate,D,26,
xoris,XOR Immediate Shift,D,27,
+343
View File
@@ -0,0 +1,343 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdbool.h>
#include <ctype.h>
#include <string.h>
int main(int argc, char *argv[]) {
printf(
"// this file is autogenerated DO NOT MODIFY\n"
"#pragma once\n"
);
FILE* fp = fopen(argv[1], "rt");
if (fp) {
char line[80];
while (fgets(line, sizeof line, fp) != NULL) {
bool with_o = strstr(line, "[o]"), with_d = strstr(line, "[.]");
char* p = strchr(line, '\n'), *name = strchr(line, ','), *mem = line;
if (p) *p = '\0';
if (name) {
*name++ = '\0';
char *form = strchr(name, ',');
if (form) {
*form++ = '\0';
char *opc = strchr(form, ',');
if (opc) {
*opc++ = '\0';
char *sec = strchr(opc, ',');
if (sec) {
struct b_info { const char *s; int o; int p; } infos[] = {
{"",1,0},
{"LT",1,12},
{"LE",2,4},
{"NG",2,4},
{"EQ",3,12},
{"GE",1,4},
{"NL",1,4},
{"GT",2,12},
{"NE",3,4},
{"SO",4,12},
{"UN",4,12},
{"NS",4,4},
{"NU",4,4},
};
#define OP_EXT ((i_opcode << 26) | (i_extopc << 1))
if (strchr(mem, '[') != NULL) *strchr(mem, '[') = '\0';
if (strchr(mem, '.') != NULL) *strchr(mem, '.') = '_';
*sec++ = '\0';
for (int i = 0; i < strlen(mem); ++i)
mem[i] = toupper(mem[i]);
int i_opcode = atoi(opc);
int i_extopc = atoi(sec);
//printf("// %s\n", mem);
if (!strcmp(form, "XO")) {
if (!strcmp(mem, "EXTSH")) {
printf(
"void %s(GPR const rt, GPR const ra) {"
" emit_%s(0x%08x, rt, ra, R0, false, false); "
"}\n"
, mem, form, OP_EXT);
printf(
"void %sC(GPR const rt, GPR const ra) {"
" emit_%s(0x%08x, rt, ra, R0, true, false); "
"}\n"
, mem, form, OP_EXT);
printf(
"void %s_(GPR const rt, GPR const ra) {"
" emit_%s(0x%08x, rt, ra, R0, false, true); "
"}\n"
, mem, form, OP_EXT);
printf(
"void %sC_(GPR const rt, GPR const ra) {"
" emit_%s(0x%08x, rt, ra, R0, true, true); "
"}\n"
, mem, form, OP_EXT);
} else {
printf(
"void %s(GPR const rt, GPR const ra, GPR const rb) {"
" emit_%s(0x%08x, rt, ra, rb, false, false); "
"}\n"
, mem, form, OP_EXT);
printf(
"void %sO(GPR const rt, GPR const ra, GPR const rb) {"
" emit_%s(0x%08x, rt, ra, rb, true, false); "
"}\n"
, mem, form, OP_EXT);
printf(
"void %s_(GPR const rt, GPR const ra, GPR const rb) {"
" emit_%s(0x%08x, rt, ra, rb, false, true); "
"}\n"
, mem, form, OP_EXT);
printf(
"void %sO_(GPR const rt, GPR const ra, GPR const rb) {"
" emit_%s(0x%08x, rt, ra, rb, true, true); "
"}\n"
, mem, form, OP_EXT);
}
} else if (!strcmp(form, "X")) {
if (!strcmp(mem, "CMPL") || !strcmp(mem, "CMP")) {
printf(
"void %s(uint32_t bf, uint32_t l, GPR const ra, GPR const rb) {"
" emit_%s(0x%08x, GPR{(bf << 2) | l}, ra, rb, false); "
"}\n"
, mem, form, OP_EXT);
} else if (!strcmp(mem, "CNTLZD") || !strcmp(mem, "CNTLZW") || !strcmp(mem, "EXTSB") || !strcmp(mem, "EXTSH") || !strcmp(mem, "EXTSW")) {
printf(
"void %s(GPR const rt, GPR const ra) {"
" emit_%s(0x%08x, ra, rt, R0, false); "
"}\n"
, mem, form, OP_EXT);
printf(
"void %s_(GPR const rt, GPR const ra) {"
" emit_%s(0x%08x, ra, rt, R0, true); "
"}\n"
, mem, form, OP_EXT);
} else {
printf(
"void %s(GPR const rt, GPR const ra, GPR const rb) {"
" emit_%s(0x%08x, ra, rt, rb, false); "
"}\n"
, mem, form, OP_EXT);
printf(
"void %s_(GPR const rt, GPR const ra, GPR const rb) {"
" emit_%s(0x%08x, ra, rt, rb, true); "
"}\n"
, mem, form, OP_EXT);
}
} else if (!strcmp(form, "I")) {
printf(
"void %s(Label const& i) {"
" emit_reloc_%s(0x%08x, i, false); "
"}\n"
, mem, form, i_opcode << 26);
printf(
"void %sL(Label const& i) {"
" emit_reloc_%s(0x%08x, i, true); "
"}\n"
, mem, form, i_opcode << 26);
} else if (!strcmp(form, "B")) {
for (int i = 0; i < 12; ++i) {
printf(
"void %s%s(CPR const cr, Label const& i) {"
" emit_reloc_%s(0x%08x, %i, cr.index + %i, i, false); "
"}\n"
, mem, infos[i].s, form, i_opcode << 26, infos[i].p, infos[i].o - 1);
printf(
"void %s%sL(CPR const cr, Label const& i) {"
" emit_reloc_%s(0x%08x, %i, cr.index + %i, i, true); "
"}\n"
, mem, infos[i].s, form, i_opcode << 26, infos[i].p, infos[i].o - 1);
if (!strcmp(mem, "BC")) mem[1] = '\0';
}
} else if (!strcmp(form, "D")) {
if (!strcmp(mem, "CMPLI") || !strcmp(mem, "CMPI")) {
printf(
"void %s(uint32_t bf, uint32_t l, GPR const ra, uint32_t d) {"
" emit_%s(0x%08x, GPR{(bf << 2) | l}, ra, d); "
"}\n"
, mem, form, i_opcode << 26);
} else if (!strcmp(mem, "ANDIS_") || !strcmp(mem, "ANDI_")
|| !strcmp(mem, "ORI") || !strcmp(mem, "ORIS")
|| !strcmp(mem, "XORI") || !strcmp(mem, "XORIS")) {
printf(
"void %s(GPR const rt, GPR const ra, uint32_t d) {"
" emit_%s(0x%08x, ra, rt, d); "
"}\n"
, mem, form, i_opcode << 26);
} else {
printf(
"void %s(GPR const rt, GPR const ra, uint32_t d) {"
" emit_%s(0x%08x, rt, ra, d); "
"}\n"
, mem, form, i_opcode << 26);
}
} else if (!strcmp(form, "SC")) {
printf(
"void %s(uint32_t lev) {"
" emit_%s(0x%08x, lev); "
"}\n"
, mem, form, i_opcode << 26);
} else if (!strcmp(form, "DS")) {
#define OP_EXT_DS ((i_opcode << 26) | (i_extopc << 0))
printf(
"void %s(GPR const rt, GPR const ra, uint32_t d) {"
" emit_%s(0x%08x, rt, ra, d >> 2); "
"}\n"
, mem, form, OP_EXT_DS);
#undef OP_EXT_DS
} else if (!strcmp(form, "XS")) {
#define OP_EXT_XS ((i_opcode << 26) | (i_extopc << 2))
/* HUGE DIFFERENCE DO NOT REMOVE */
printf(
"void %s(GPR const rt, GPR const ra, uint32_t sh) {"
" emit_%s(0x%08x, rt, ra, sh, false); "
"}\n"
, mem, form, OP_EXT_XS);
printf(
"void %s_(GPR const rt, GPR const ra, uint32_t sh) {"
" emit_%s(0x%08x, rt, ra, sh, true); "
"}\n"
, mem, form, OP_EXT_XS);
#undef OP_EXT_XS
} else if (!strcmp(form, "XL")) {
if (!strcmp(mem, "BCLR")) {
printf(
"void %s(GPR const bt, CPR const ba, GPR const bb) {"
" emit_%s(0x%08x, bt.index, ba.index, bb.index, false); "
"}\n"
, mem, form, OP_EXT);
printf(
"void %sL(GPR const bt, CPR const ba, GPR const bb) {"
" emit_%s(0x%08x, bt.index, ba.index, bb.index, true); "
"}\n"
, mem, form, OP_EXT);
if (!strcmp(mem, "BCLR")) mem[1] = '\0';
for (int i = 1; i < 12; ++i) {
printf(
"void %s%sLR(CPR const cr) {"
" emit_%s(0x%08x, %i, cr.index + %i, 0, false); "
"}\n"
, mem, infos[i].s, form, OP_EXT, infos[i].p, infos[i].o - 1);
printf(
"void %s%sLRL(CPR const cr) {"
" emit_%s(0x%08x, %i, cr.index + %i, 0, true); "
"}\n"
, mem, infos[i].s, form, OP_EXT, infos[i].p, infos[i].o - 1);
}
} else if (mem[0] == 'B') {
printf(
"void %s(GPR const bt, CPR const ba, GPR const bb) {"
" emit_%s(0x%08x, bt.index, ba.index, bb.index, false); "
"}\n"
, mem, form, OP_EXT);
printf(
"void %sL(GPR const bt, CPR const ba, GPR const bb) {"
" emit_%s(0x%08x, bt.index, ba.index, bb.index, true); "
"}\n"
, mem, form, OP_EXT);
printf(
"void %s(GPR const bt, Cond const ba, GPR const bb) {"
" emit_%s(0x%08x, bt.index, cond2offset(ba), bb.index, false); "
"}\n"
, mem, form, OP_EXT);
printf(
"void %sL(GPR const bt, Cond const ba, GPR const bb) {"
" emit_%s(0x%08x, bt.index, cond2offset(ba), bb.index, true); "
"}\n"
, mem, form, OP_EXT);
} else {
printf(
"void %s(CPR const bt, CPR const ba, CPR const bb) {"
" emit_%s(0x%08x, bt.index, ba.index, bb.index, false); "
"}\n"
, mem, form, OP_EXT);
printf(
"void %sL(CPR const bt, CPR const ba, CPR const bb) {"
" emit_%s(0x%08x, bt.index, ba.index, bb.index, true); "
"}\n"
, mem, form, OP_EXT);
}
} else if (!strcmp(form, "M")) {
if (!strcmp(mem, "RLWNM")) {
printf(
"void %s(GPR const rs, GPR const ra, GPR const rb, uint32_t mb, uint32_t me = 0) {"
" emit_%s(0x%08x, ra, rs, rb.index, mb, me, false); "
"}\n"
, mem, form, OP_EXT);
printf(
"void %s_(GPR const rs, GPR const ra, GPR const rb, uint32_t mb, uint32_t me = 0) {"
" emit_%s(0x%08x, ra, rs, rb.index, mb, me, true); "
"}\n"
, mem, form, OP_EXT);
} else {
printf(
"void %s(GPR const rs, GPR const ra, uint32_t sh, uint32_t mb, uint32_t me = 0) {"
" emit_%s(0x%08x, ra, rs, sh, mb, me, false); "
"}\n"
, mem, form, OP_EXT);
printf(
"void %s_(GPR const rs, GPR const ra, uint32_t sh, uint32_t mb, uint32_t me = 0) {"
" emit_%s(0x%08x, ra, rs, sh, mb, me, true); "
"}\n"
, mem, form, OP_EXT);
}
} else if (!strcmp(form, "MD")) {
printf(
"void %s(GPR const rs, GPR const ra, uint32_t mb, uint32_t sh) {"
" emit_%s(0x%08x, ra, rs, mb, sh, false); "
"}\n"
, mem, form, (i_opcode << 26) | (i_extopc << 2));
printf(
"void %s_(GPR const rs, GPR const ra, uint32_t mb, uint32_t sh) {"
" emit_%s(0x%08x, ra, rs, mb, sh, true); "
"}\n"
, mem, form, (i_opcode << 26) | (i_extopc << 2));
} else if (!strcmp(form, "MDS")) {
printf(
"void %s(GPR const rs, GPR const ra, GPR const rb, uint32_t mb) {"
" emit_%s(0x%08x, ra, rs, rb, mb, false); "
"}\n"
, mem, form, OP_EXT);
printf(
"void %s_(GPR const rs, GPR const ra, GPR const rb, uint32_t mb) {"
" emit_%s(0x%08x, ra, rs, rb, mb, true); "
"}\n"
, mem, form, OP_EXT);
} else if (!strcmp(form, "A")) {
printf(
"void %s(FPR const frt, FPR const fra, FPR const frb, FPR const frc) {"
" emit_%s(0x%08x, frt, fra, frb, frc, false); "
"}\n"
, mem, form, OP_EXT);
printf(
"void %s_(FPR const frt, FPR const fra, FPR const frb, FPR const frc) {"
" emit_%s(0x%08x, frt, fra, frb, frc, true); "
"}\n"
, mem, form, OP_EXT);
} else if (!strcmp(form, "XFX")) {
printf(
"void %s(GPR const rt, uint32_t spr) {"
" emit_%s(0x%08x, rt, spr); "
"}\n"
, mem, form, OP_EXT);
} else {
printf(
"void %s() {"
" emit_%s(0x%08x); "
"}\n"
, mem, form, OP_EXT);
}
}
}
}
}
//printf("%s\n", line);
}
fclose(fp);
return 0;
}
return 1;
}
+401
View File
@@ -0,0 +1,401 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#undef NDEBUG
#include <cstdint>
#include <cstddef>
#include <cassert>
#include <vector>
#include <utility>
#include <sys/mman.h>
#include <cstdlib>
//#ifndef __cpp_lib_unreachable
namespace std {
[[noreturn]] inline void unreachable() {
#if defined(_MSC_VER) && !defined(__clang__) // MSVC
__assume(false);
#else // GCC, Clang
__builtin_unreachable();
#endif
}
}
//#endif
namespace powah {
// Symbolic conditions
enum class Cond : uint8_t {
LT, LE, NG, EQ, GE, NL, GT, NE, SO, UN, NS, NU
};
struct GPR { uint32_t index; };
struct FPR { uint32_t index; };
struct VPR { uint32_t index; };
struct CPR { uint32_t index; };
struct Label { uint32_t index; };
constexpr inline GPR R0{0};
constexpr inline GPR R1{1};
constexpr inline GPR R2{2};
constexpr inline GPR R3{3};
constexpr inline GPR R4{4};
constexpr inline GPR R5{5};
constexpr inline GPR R6{6};
constexpr inline GPR R7{7};
constexpr inline GPR R8{8};
constexpr inline GPR R9{9};
constexpr inline GPR R10{10};
constexpr inline GPR R11{11};
constexpr inline GPR R12{12};
constexpr inline GPR R13{13};
constexpr inline GPR R14{14};
constexpr inline GPR R15{15};
constexpr inline GPR R16{16};
constexpr inline GPR R17{17};
constexpr inline GPR R18{18};
constexpr inline GPR R19{19};
constexpr inline GPR R20{20};
constexpr inline GPR R21{21};
constexpr inline GPR R22{22};
constexpr inline GPR R23{23};
constexpr inline GPR R24{24};
constexpr inline GPR R25{25};
constexpr inline GPR R26{26};
constexpr inline GPR R27{27};
constexpr inline GPR R28{28};
constexpr inline GPR R29{29};
constexpr inline GPR R30{30};
constexpr inline GPR R31{31};
constexpr inline FPR FR0{0};
constexpr inline FPR FR1{1};
constexpr inline FPR FR2{2};
constexpr inline FPR FR3{3};
constexpr inline FPR FR4{4};
constexpr inline FPR FR5{5};
constexpr inline FPR FR6{6};
constexpr inline FPR FR7{7};
constexpr inline FPR FR8{8};
constexpr inline FPR FR9{9};
constexpr inline FPR FR10{10};
constexpr inline FPR FR11{11};
constexpr inline FPR FR12{12};
constexpr inline FPR FR13{13};
constexpr inline FPR FR14{14};
constexpr inline FPR FR15{15};
constexpr inline FPR FR16{16};
constexpr inline FPR FR17{17};
constexpr inline FPR FR18{18};
constexpr inline FPR FR19{19};
constexpr inline FPR FR20{20};
constexpr inline FPR FR21{21};
constexpr inline FPR FR22{22};
constexpr inline FPR FR23{23};
constexpr inline FPR FR24{24};
constexpr inline FPR FR25{25};
constexpr inline FPR FR26{26};
constexpr inline FPR FR27{27};
constexpr inline FPR FR28{28};
constexpr inline FPR FR29{29};
constexpr inline FPR FR30{30};
constexpr inline FPR FR31{31};
// They call it CPR because when programmers see this code they-
constexpr inline CPR CR0{0};
constexpr inline CPR CR1{4};
constexpr inline CPR CR2{8};
constexpr inline CPR CR3{12};
constexpr inline CPR CR4{16};
constexpr inline CPR CR5{20};
constexpr inline CPR CR6{24};
constexpr inline CPR CR7{28};
constexpr uint32_t XER_SO = 32;
constexpr uint32_t XER_OV = 33;
constexpr uint32_t XER_CA = 34;
enum class RelocKind : uint8_t {
FormB,
FormI,
};
struct RelocInfo {
uint32_t offset;
RelocKind kind;
};
struct Context {
Context() = default;
Context(void* ptr, size_t size)
: base{reinterpret_cast<uint32_t*>(ptr)}
, offset{0}
, size{uint32_t(size)} {
}
~Context() = default;
std::vector<uint32_t> labels;
std::vector<std::pair<uint32_t, RelocInfo>> relocs;
Label DefineLabel() {
labels.push_back(0);
return Label{ uint32_t(labels.size() - 1) };
}
void ApplyRelocs() {
for (auto const &[index, info] : relocs) {
//assert(labels[index] != 0); //label must have an addr
int32_t rel = (int32_t)labels[index] - (int32_t)info.offset;
switch (info.kind) {
case RelocKind::FormB:
base[info.offset] |= bitExt(rel, 16, 14);
break;
case RelocKind::FormI:
base[info.offset] |= bitExt(rel, 6, 24);
break;
}
}
relocs.clear();
}
void LABEL(Label l) {
assert(labels[l.index] == 0);
labels[l.index] = offset;
}
static constexpr uint32_t cond2offset(Cond c) noexcept {
switch (c) {
case Cond::LT: return 1;
case Cond::LE: return 2;
case Cond::NG: return 2;
case Cond::EQ: return 3;
case Cond::GE: return 1;
case Cond::NL: return 1;
case Cond::GT: return 2;
case Cond::NE: return 3;
case Cond::SO: return 4;
case Cond::UN: return 4;
case Cond::NS: return 4;
case Cond::NU: return 4;
default: return 0; //hopefully icc isn't stupid
}
}
uint32_t bitExt(uint32_t value, uint32_t offs, uint32_t n) {
uint32_t mask = (1UL << n) - 1;
return (value & mask) << (32 - (n + offs));
}
void emit_XO(uint32_t op, GPR const rt, GPR const ra, GPR const rb, bool oe, bool rc) {
base[offset++] = (op |
bitExt(rt.index, 6, 5)
| bitExt(ra.index, 11, 5)
| bitExt(rb.index, 16, 5)
| bitExt(oe, 21, 1)
| bitExt(rc, 31, 1)
);
}
void emit_D(uint32_t op, GPR const rt, GPR const ra, uint32_t d) {
base[offset++] = (op |
bitExt(rt.index, 6, 5)
| bitExt(ra.index, 11, 5)
| (d & 0xffff)
);
}
void emit_X(uint32_t op, GPR const rt, GPR const ra, GPR const rb, bool rc) {
base[offset++] = (op |
bitExt(rt.index, 6, 5)
| bitExt(ra.index, 11, 5)
| bitExt(rb.index, 16, 5)
| bitExt(rc, 31, 1)
);
}
void emit_XS(uint32_t op, GPR const rt, GPR const ra, uint32_t sh, bool rc) {
base[offset++] = (op |
((rt.index & 0x1f) << 16)
| ((ra.index & 0x1f) << 21)
| ((sh & 0x1f) << 11)
| ((sh >> 4) & 0x02)
| bitExt(rc, 31, 1)
);
}
void emit_reloc_I(uint32_t op, Label const& l, bool lk) {
relocs.emplace_back(l.index, RelocInfo{ offset, RelocKind::FormI });
base[offset++] = (op |
bitExt(lk, 31, 1)
);
}
void emit_reloc_B(uint32_t op, uint32_t bo, uint32_t cri, Label const& l, bool lk) {
relocs.emplace_back(l.index, RelocInfo{ offset, RelocKind::FormB });
base[offset++] = (op |
bitExt(bo, 6, 5)
| bitExt(cri, 11, 5)
| bitExt(lk, 31, 1)
);
}
void emit_XL(uint32_t op, uint32_t bt, uint32_t ba, uint32_t bb, bool lk) {
base[offset++] = (op |
bitExt(bt, 6, 5)
| bitExt(ba, 11, 5)
| bitExt(bb, 16, 5)
| bitExt(lk, 31, 1)
);
}
void emit_A(uint32_t op, FPR const frt, FPR const fra, FPR const frb, FPR const frc, bool rc) {
base[offset++] = (op |
bitExt(frt.index, 6, 5)
| bitExt(fra.index, 11, 5)
| bitExt(frb.index, 16, 5)
| bitExt(frc.index, 21, 5)
| bitExt(rc, 31, 1)
);
}
void emit_DS(uint32_t op, GPR const rt, GPR const ra, uint32_t d) {
//assert(d & 0x03 == 0);
base[offset++] = (op |
bitExt(rt.index, 6, 5)
| bitExt(ra.index, 11, 5)
| bitExt(d, 16, 14)
);
}
void emit_M(uint32_t op, GPR const rs, GPR const ra, uint32_t sh, uint32_t mb, uint32_t me, bool rc) {
assert(sh <= 0x3f && mb <= 0x3f);
base[offset++] = (op |
bitExt(rs.index, 6, 5)
| bitExt(ra.index, 11, 5)
| ((sh & 0x1f) << 11)
| ((mb & 0x1f) << 6)
| ((me & 0x1f) << 1)
| bitExt(rc, 31, 1)
);
}
void emit_MD(uint32_t op, GPR const rs, GPR const ra, GPR const rb, uint32_t mb, bool rc) {
assert(mb <= 0x3f);
base[offset++] = (op |
bitExt(rs.index, 6, 5)
| bitExt(ra.index, 11, 5)
| bitExt(rb.index, 16, 5)
| ((mb & 0x1f) << 6) | (mb & 0x20)
| bitExt(rc, 31, 1)
);
}
void emit_MD(uint32_t op, GPR const rs, GPR const ra, uint32_t sh, uint32_t mb, bool rc) {
assert(sh <= 0x3f && mb <= 0x3f);
base[offset++] = (op |
bitExt(rs.index, 6, 5)
| bitExt(ra.index, 11, 5)
| ((mb & 0x1f) << 6) | (mb & 0x20)
| ((sh & 0x1f) << 11) | ((sh >> 4) & 0x02)
| bitExt(rc, 31, 1)
);
}
void emit_MDS(uint32_t op, GPR const rs, GPR const ra, GPR const rb, uint32_t mb, bool rc) {
base[offset++] = (op |
bitExt(rs.index, 6, 5)
| bitExt(ra.index, 11, 5)
| bitExt(rb.index, 16, 5)
| ((mb & 0x1f) << 6) | (mb & 0x20)
| bitExt(rc, 31, 1)
);
}
void emit_XFL(uint32_t op) {
(void)op;
std::abort();
}
void emit_XFX(uint32_t op, GPR const rt, uint32_t spr) {
base[offset++] = (op |
(rt.index & 0x1f) << 21
| ((spr & 0xff) << 12)
);
}
void emit_SC(uint32_t op, uint32_t lev) {
(void)op;
(void)lev;
std::abort();
}
// Extended Memmonics, hand coded :)
void MR(GPR const ra, GPR const rs) { OR(ra, rs, rs); }
void NOP() { ORI(R0, R0, 0); }
void NOT(GPR const ra, GPR const rs) { NOR(ra, rs, rs); }
void ROTLDI(GPR const ra, GPR const rs, uint32_t n) { RLDICL(ra, rs, n, 0); }
void ROTRDI(GPR const ra, GPR const rs, uint32_t n) { RLDICL(ra, rs, 64 - n, 0); }
void ROTLWI(GPR const ra, GPR const rs, uint32_t n) { RLWINM(ra, rs, n, 0, 31); }
void ROTRWI(GPR const ra, GPR const rs, uint32_t n) { RLWINM(ra, rs, 32 - n, 0, 31); }
void ROTLW(GPR const ra, GPR const rs, GPR const rb) { RLWNM(ra, rs, rb, 0, 31); }
void ROTLD(GPR const ra, GPR const rs, GPR const rb) { RLDCL(ra, rs, rb, 0); }
void EXTLDI(GPR const ra, GPR const rs, uint32_t n, uint32_t b) { RLDICR(ra, rs, b, n - 1); }
void SLDI(GPR const ra, GPR const rs, uint32_t n) { RLDICR(ra, rs, n, 63 - n); }
void CLRLDI(GPR const ra, GPR const rs, uint32_t n) { RLDICL(ra, rs, 0, n); }
void EXTRDI(GPR const ra, GPR const rs, uint32_t n, uint32_t b) { RLDICL(ra, rs, b + n, 64 - n); }
void SRDI(GPR const ra, GPR const rs, uint32_t n) { RLDICL(ra, rs, 64 - n, n); }
void CLLDI(GPR const ra, GPR const rs, uint32_t n) { RLDICR(ra, rs, 0, n); }
void CLRSLDI(GPR const ra, GPR const rs, uint32_t n, uint32_t b) { RLDICR(ra, rs, n, b - n); }
void EXTLWI(GPR const ra, GPR const rs, uint32_t n, uint32_t b) { RLWINM(ra, rs, b, 0, n - 1); }
void SRWI(GPR const ra, GPR const rs, uint32_t n) { RLWINM(ra, rs, 32 - n, n, 31); }
void CLRRWI(GPR const ra, GPR const rs, uint32_t n) { RLWINM(ra, rs, 0, 0, 31 - n); }
void CRSET(CPR const bx) { CREQV(bx, bx, bx); }
void CRCLR(CPR const bx) { CRXOR(bx, bx, bx); }
void CRMOVE(CPR const bx, CPR const by) { CROR(bx, by, by); }
void CRNOT(CPR const bx, CPR const by) { CRNOR(bx, by, by); }
void CMPLDI(CPR const cr, GPR const rx, uint32_t v) { CMPLI(cr.index, 1, rx, v); }
void CMPLWI(CPR const cr, GPR const rx, uint32_t v) { CMPLI(cr.index, 0, rx, v); }
void CMPLD(CPR const cr, GPR const rx, GPR const ry) { CMPL(cr.index, 1, rx, ry); }
void CMPLW(CPR const cr, GPR const rx, GPR const ry) { CMPL(cr.index, 0, rx, ry); }
void CMPLDI(GPR const rx, uint32_t v) { CMPLI(0, 1, rx, v); }
void CMPLWI(GPR const rx, uint32_t v) { CMPLI(0, 0, rx, v); }
void CMPLD(GPR const rx, GPR const ry) { CMPL(0, 1, rx, ry); }
void CMPLW(GPR const rx, GPR const ry) { CMPL(0, 0, rx, ry); }
void CMPWI(CPR const cr, GPR const rx, uint32_t si) { CMPI(cr.index, 0, rx, si); }
void CMPW(CPR const cr, GPR const rx, GPR const ry) { CMP(cr.index, 0, rx, ry); }
void CMPDI(CPR const cr, GPR const rx, uint32_t si) { CMPI(cr.index, 1, rx, si); }
void CMPD(CPR const cr, GPR const rx, GPR const ry) { CMP(cr.index, 1, rx, ry); }
void CMPWI(GPR const rx, uint32_t si) { CMPI(0, 0, rx, si); }
void CMPW(GPR const rx, GPR const ry) { CMP(0, 0, rx, ry); }
void CMPDI(GPR const rx, uint32_t si) { CMPI(0, 1, rx, si); }
void CMPD(GPR const rx, GPR const ry) { CMP(0, 1, rx, ry); }
void LI(GPR const rx, uint32_t value) { ADDI(rx, R0, value); }
void LIS(GPR const rx, uint32_t value) { ADDIS(rx, R0, value); }
void MFLR(GPR const rt) { MFSPR(powah::GPR{8}, rt, powah::GPR{0}); }
void MTLR(GPR const rt) { MTSPR(powah::GPR{8}, rt, powah::GPR{0}); }
void BLR() { base[offset++] = 0x4e800020; } //BCLR(R0, CR0, R0);
void MFCTR(GPR const rt) { MFSPR(powah::GPR{9}, rt, powah::GPR{0}); }
void MTCTR(GPR const rt) { MTSPR(powah::GPR{9}, rt, powah::GPR{0}); }
void BCTRL() { base[offset++] = 0x4e800421; } //BCCTRL(R0, CR0, R0);
void BCTR() { base[offset++] = 0x4e800420; } //BCCTR(R0, CR0, R0);
// TODO: PowerPC 11 stuff
void ISEL(GPR const rd, GPR const ra, GPR const rb, uint32_t d) {
(void)rd;
(void)ra;
(void)rb;
(void)d;
std::unreachable();
}
void ISELLT(GPR const rd, GPR const ra, GPR const rb) { ISEL(rd, ra, rb, 0); }
void ISELGT(GPR const rd, GPR const ra, GPR const rb) { ISEL(rd, ra, rb, 1); }
void ISELEQ(GPR const rd, GPR const ra, GPR const rb) { ISEL(rd, ra, rb, 2); }
// Rawly pasted because fuck you
#include "powah_gen_base.hpp"
uint32_t* base = nullptr;
uint32_t offset = 0;
uint32_t size = 0;
};
}
+487
View File
@@ -0,0 +1,487 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// this file is autogenerated DO NOT MODIFY
#pragma once
void ADD(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000214, rt, ra, rb, false, false); }
void ADDO(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000214, rt, ra, rb, true, false); }
void ADD_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000214, rt, ra, rb, false, true); }
void ADDO_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000214, rt, ra, rb, true, true); }
void ADDC(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000014, rt, ra, rb, false, false); }
void ADDCO(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000014, rt, ra, rb, true, false); }
void ADDC_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000014, rt, ra, rb, false, true); }
void ADDCO_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000014, rt, ra, rb, true, true); }
void ADDE(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000114, rt, ra, rb, false, false); }
void ADDEO(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000114, rt, ra, rb, true, false); }
void ADDE_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000114, rt, ra, rb, false, true); }
void ADDEO_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000114, rt, ra, rb, true, true); }
void ADDI(GPR const rt, GPR const ra, uint32_t d) { emit_D(0x38000000, rt, ra, d); }
void ADDIC(GPR const rt, GPR const ra, uint32_t d) { emit_D(0x30000000, rt, ra, d); }
void ADDIC_(GPR const rt, GPR const ra, uint32_t d) { emit_D(0x34000000, rt, ra, d); }
void ADDIS(GPR const rt, GPR const ra, uint32_t d) { emit_D(0x3c000000, rt, ra, d); }
void ADDME(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0001d4, rt, ra, rb, false, false); }
void ADDMEO(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0001d4, rt, ra, rb, true, false); }
void ADDME_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0001d4, rt, ra, rb, false, true); }
void ADDMEO_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0001d4, rt, ra, rb, true, true); }
void ADDZE(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000194, rt, ra, rb, false, false); }
void ADDZEO(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000194, rt, ra, rb, true, false); }
void ADDZE_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000194, rt, ra, rb, false, true); }
void ADDZEO_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000194, rt, ra, rb, true, true); }
void AND(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000038, ra, rt, rb, false); }
void AND_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000038, ra, rt, rb, true); }
void ANDC(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000078, ra, rt, rb, false); }
void ANDC_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000078, ra, rt, rb, true); }
void ANDI_(GPR const rt, GPR const ra, uint32_t d) { emit_D(0x70000000, ra, rt, d); }
void ANDIS_(GPR const rt, GPR const ra, uint32_t d) { emit_D(0x74000000, ra, rt, d); }
void B(Label const& i) { emit_reloc_I(0x48000000, i, false); }
void BL(Label const& i) { emit_reloc_I(0x48000000, i, true); }
void BC(CPR const cr, Label const& i) { emit_reloc_B(0x40000000, 0, cr.index + 0, i, false); }
void BCL(CPR const cr, Label const& i) { emit_reloc_B(0x40000000, 0, cr.index + 0, i, true); }
void BLT(CPR const cr, Label const& i) { emit_reloc_B(0x40000000, 12, cr.index + 0, i, false); }
void BLTL(CPR const cr, Label const& i) { emit_reloc_B(0x40000000, 12, cr.index + 0, i, true); }
void BLE(CPR const cr, Label const& i) { emit_reloc_B(0x40000000, 4, cr.index + 1, i, false); }
void BLEL(CPR const cr, Label const& i) { emit_reloc_B(0x40000000, 4, cr.index + 1, i, true); }
void BNG(CPR const cr, Label const& i) { emit_reloc_B(0x40000000, 4, cr.index + 1, i, false); }
void BNGL(CPR const cr, Label const& i) { emit_reloc_B(0x40000000, 4, cr.index + 1, i, true); }
void BEQ(CPR const cr, Label const& i) { emit_reloc_B(0x40000000, 12, cr.index + 2, i, false); }
void BEQL(CPR const cr, Label const& i) { emit_reloc_B(0x40000000, 12, cr.index + 2, i, true); }
void BGE(CPR const cr, Label const& i) { emit_reloc_B(0x40000000, 4, cr.index + 0, i, false); }
void BGEL(CPR const cr, Label const& i) { emit_reloc_B(0x40000000, 4, cr.index + 0, i, true); }
void BNL(CPR const cr, Label const& i) { emit_reloc_B(0x40000000, 4, cr.index + 0, i, false); }
void BNLL(CPR const cr, Label const& i) { emit_reloc_B(0x40000000, 4, cr.index + 0, i, true); }
void BGT(CPR const cr, Label const& i) { emit_reloc_B(0x40000000, 12, cr.index + 1, i, false); }
void BGTL(CPR const cr, Label const& i) { emit_reloc_B(0x40000000, 12, cr.index + 1, i, true); }
void BNE(CPR const cr, Label const& i) { emit_reloc_B(0x40000000, 4, cr.index + 2, i, false); }
void BNEL(CPR const cr, Label const& i) { emit_reloc_B(0x40000000, 4, cr.index + 2, i, true); }
void BSO(CPR const cr, Label const& i) { emit_reloc_B(0x40000000, 12, cr.index + 3, i, false); }
void BSOL(CPR const cr, Label const& i) { emit_reloc_B(0x40000000, 12, cr.index + 3, i, true); }
void BUN(CPR const cr, Label const& i) { emit_reloc_B(0x40000000, 12, cr.index + 3, i, false); }
void BUNL(CPR const cr, Label const& i) { emit_reloc_B(0x40000000, 12, cr.index + 3, i, true); }
void BNS(CPR const cr, Label const& i) { emit_reloc_B(0x40000000, 4, cr.index + 3, i, false); }
void BNSL(CPR const cr, Label const& i) { emit_reloc_B(0x40000000, 4, cr.index + 3, i, true); }
void BCCTR(GPR const bt, CPR const ba, GPR const bb) { emit_XL(0x4c000420, bt.index, ba.index, bb.index, false); }
void BCCTRL(GPR const bt, CPR const ba, GPR const bb) { emit_XL(0x4c000420, bt.index, ba.index, bb.index, true); }
void BCCTR(GPR const bt, Cond const ba, GPR const bb) { emit_XL(0x4c000420, bt.index, cond2offset(ba), bb.index, false); }
void BCCTRL(GPR const bt, Cond const ba, GPR const bb) { emit_XL(0x4c000420, bt.index, cond2offset(ba), bb.index, true); }
void BCLR(GPR const bt, CPR const ba, GPR const bb) { emit_XL(0x4c000020, bt.index, ba.index, bb.index, false); }
void BCLRL(GPR const bt, CPR const ba, GPR const bb) { emit_XL(0x4c000020, bt.index, ba.index, bb.index, true); }
void BLTLR(CPR const cr) { emit_XL(0x4c000020, 12, cr.index + 0, 0, false); }
void BLTLRL(CPR const cr) { emit_XL(0x4c000020, 12, cr.index + 0, 0, true); }
void BLELR(CPR const cr) { emit_XL(0x4c000020, 4, cr.index + 1, 0, false); }
void BLELRL(CPR const cr) { emit_XL(0x4c000020, 4, cr.index + 1, 0, true); }
void BNGLR(CPR const cr) { emit_XL(0x4c000020, 4, cr.index + 1, 0, false); }
void BNGLRL(CPR const cr) { emit_XL(0x4c000020, 4, cr.index + 1, 0, true); }
void BEQLR(CPR const cr) { emit_XL(0x4c000020, 12, cr.index + 2, 0, false); }
void BEQLRL(CPR const cr) { emit_XL(0x4c000020, 12, cr.index + 2, 0, true); }
void BGELR(CPR const cr) { emit_XL(0x4c000020, 4, cr.index + 0, 0, false); }
void BGELRL(CPR const cr) { emit_XL(0x4c000020, 4, cr.index + 0, 0, true); }
void BNLLR(CPR const cr) { emit_XL(0x4c000020, 4, cr.index + 0, 0, false); }
void BNLLRL(CPR const cr) { emit_XL(0x4c000020, 4, cr.index + 0, 0, true); }
void BGTLR(CPR const cr) { emit_XL(0x4c000020, 12, cr.index + 1, 0, false); }
void BGTLRL(CPR const cr) { emit_XL(0x4c000020, 12, cr.index + 1, 0, true); }
void BNELR(CPR const cr) { emit_XL(0x4c000020, 4, cr.index + 2, 0, false); }
void BNELRL(CPR const cr) { emit_XL(0x4c000020, 4, cr.index + 2, 0, true); }
void BSOLR(CPR const cr) { emit_XL(0x4c000020, 12, cr.index + 3, 0, false); }
void BSOLRL(CPR const cr) { emit_XL(0x4c000020, 12, cr.index + 3, 0, true); }
void BUNLR(CPR const cr) { emit_XL(0x4c000020, 12, cr.index + 3, 0, false); }
void BUNLRL(CPR const cr) { emit_XL(0x4c000020, 12, cr.index + 3, 0, true); }
void BNSLR(CPR const cr) { emit_XL(0x4c000020, 4, cr.index + 3, 0, false); }
void BNSLRL(CPR const cr) { emit_XL(0x4c000020, 4, cr.index + 3, 0, true); }
void CMP(uint32_t bf, uint32_t l, GPR const ra, GPR const rb) { emit_X(0x7c000000, GPR{(bf << 2) | l}, ra, rb, false); }
void CMPI(uint32_t bf, uint32_t l, GPR const ra, uint32_t d) { emit_D(0x2c000000, GPR{(bf << 2) | l}, ra, d); }
void CMPL(uint32_t bf, uint32_t l, GPR const ra, GPR const rb) { emit_X(0x7c000040, GPR{(bf << 2) | l}, ra, rb, false); }
void CMPLI(uint32_t bf, uint32_t l, GPR const ra, uint32_t d) { emit_D(0x28000000, GPR{(bf << 2) | l}, ra, d); }
void CNTLZD(GPR const rt, GPR const ra) { emit_X(0x7c000074, ra, rt, R0, false); }
void CNTLZD_(GPR const rt, GPR const ra) { emit_X(0x7c000074, ra, rt, R0, true); }
void CNTLZW(GPR const rt, GPR const ra) { emit_X(0x7c000034, ra, rt, R0, false); }
void CNTLZW_(GPR const rt, GPR const ra) { emit_X(0x7c000034, ra, rt, R0, true); }
void CRAND(CPR const bt, CPR const ba, CPR const bb) { emit_XL(0x4c000202, bt.index, ba.index, bb.index, false); }
void CRANDL(CPR const bt, CPR const ba, CPR const bb) { emit_XL(0x4c000202, bt.index, ba.index, bb.index, true); }
void CRANDC(CPR const bt, CPR const ba, CPR const bb) { emit_XL(0x4c000102, bt.index, ba.index, bb.index, false); }
void CRANDCL(CPR const bt, CPR const ba, CPR const bb) { emit_XL(0x4c000102, bt.index, ba.index, bb.index, true); }
void CREQV(CPR const bt, CPR const ba, CPR const bb) { emit_XL(0x4c000242, bt.index, ba.index, bb.index, false); }
void CREQVL(CPR const bt, CPR const ba, CPR const bb) { emit_XL(0x4c000242, bt.index, ba.index, bb.index, true); }
void CRNAND(CPR const bt, CPR const ba, CPR const bb) { emit_XL(0x4c0001c2, bt.index, ba.index, bb.index, false); }
void CRNANDL(CPR const bt, CPR const ba, CPR const bb) { emit_XL(0x4c0001c2, bt.index, ba.index, bb.index, true); }
void CRNOR(CPR const bt, CPR const ba, CPR const bb) { emit_XL(0x4c000042, bt.index, ba.index, bb.index, false); }
void CRNORL(CPR const bt, CPR const ba, CPR const bb) { emit_XL(0x4c000042, bt.index, ba.index, bb.index, true); }
void CROR(CPR const bt, CPR const ba, CPR const bb) { emit_XL(0x4c000382, bt.index, ba.index, bb.index, false); }
void CRORL(CPR const bt, CPR const ba, CPR const bb) { emit_XL(0x4c000382, bt.index, ba.index, bb.index, true); }
void CRORC(CPR const bt, CPR const ba, CPR const bb) { emit_XL(0x4c000342, bt.index, ba.index, bb.index, false); }
void CRORCL(CPR const bt, CPR const ba, CPR const bb) { emit_XL(0x4c000342, bt.index, ba.index, bb.index, true); }
void CRXOR(CPR const bt, CPR const ba, CPR const bb) { emit_XL(0x4c000182, bt.index, ba.index, bb.index, false); }
void CRXORL(CPR const bt, CPR const ba, CPR const bb) { emit_XL(0x4c000182, bt.index, ba.index, bb.index, true); }
void DCBF(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0000ac, ra, rt, rb, false); }
void DCBF_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0000ac, ra, rt, rb, true); }
void DCBI(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0003ac, ra, rt, rb, false); }
void DCBI_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0003ac, ra, rt, rb, true); }
void DCBST(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00006c, ra, rt, rb, false); }
void DCBST_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00006c, ra, rt, rb, true); }
void DCBT(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00022c, ra, rt, rb, false); }
void DCBT_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00022c, ra, rt, rb, true); }
void DCBTST(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0001ec, ra, rt, rb, false); }
void DCBTST_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0001ec, ra, rt, rb, true); }
void DCBZ(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0007ec, ra, rt, rb, false); }
void DCBZ_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0007ec, ra, rt, rb, true); }
void DIVD(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0003d2, rt, ra, rb, false, false); }
void DIVDO(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0003d2, rt, ra, rb, true, false); }
void DIVD_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0003d2, rt, ra, rb, false, true); }
void DIVDO_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0003d2, rt, ra, rb, true, true); }
void DIVDU(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000392, rt, ra, rb, false, false); }
void DIVDUO(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000392, rt, ra, rb, true, false); }
void DIVDU_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000392, rt, ra, rb, false, true); }
void DIVDUO_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000392, rt, ra, rb, true, true); }
void DIVW(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0003d6, rt, ra, rb, false, false); }
void DIVWO(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0003d6, rt, ra, rb, true, false); }
void DIVW_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0003d6, rt, ra, rb, false, true); }
void DIVWO_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0003d6, rt, ra, rb, true, true); }
void DIVWU(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000396, rt, ra, rb, false, false); }
void DIVWUO(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000396, rt, ra, rb, true, false); }
void DIVWU_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000396, rt, ra, rb, false, true); }
void DIVWUO_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000396, rt, ra, rb, true, true); }
void ECIWX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00026c, ra, rt, rb, false); }
void ECIWX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00026c, ra, rt, rb, true); }
void ECOWX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00036c, ra, rt, rb, false); }
void ECOWX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00036c, ra, rt, rb, true); }
void EIEIO(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0006ac, ra, rt, rb, false); }
void EIEIO_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0006ac, ra, rt, rb, true); }
void EQV(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000238, ra, rt, rb, false); }
void EQV_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000238, ra, rt, rb, true); }
void EXTSB(GPR const rt, GPR const ra) { emit_X(0x7c000774, ra, rt, R0, false); }
void EXTSB_(GPR const rt, GPR const ra) { emit_X(0x7c000774, ra, rt, R0, true); }
void EXTSH(GPR const rt, GPR const ra) { emit_XO(0x7c000734, rt, ra, R0, false, false); }
void EXTSHC(GPR const rt, GPR const ra) { emit_XO(0x7c000734, rt, ra, R0, true, false); }
void EXTSH_(GPR const rt, GPR const ra) { emit_XO(0x7c000734, rt, ra, R0, false, true); }
void EXTSHC_(GPR const rt, GPR const ra) { emit_XO(0x7c000734, rt, ra, R0, true, true); }
void EXTSW(GPR const rt, GPR const ra) { emit_X(0x7c0007b4, ra, rt, R0, false); }
void EXTSW_(GPR const rt, GPR const ra) { emit_X(0x7c0007b4, ra, rt, R0, true); }
void FABS(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc000210, ra, rt, rb, false); }
void FABS_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc000210, ra, rt, rb, true); }
void FADD(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xfc00002a, frt, fra, frb, frc, false); }
void FADD_(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xfc00002a, frt, fra, frb, frc, true); }
void FADDS(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xec00002a, frt, fra, frb, frc, false); }
void FADDS_(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xec00002a, frt, fra, frb, frc, true); }
void FCFID(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc00069c, ra, rt, rb, false); }
void FCFID_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc00069c, ra, rt, rb, true); }
void FCMPO(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc000040, ra, rt, rb, false); }
void FCMPO_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc000040, ra, rt, rb, true); }
void FCMPU(CPR const bt, CPR const ba, CPR const bb) { emit_XL(0xfc000000, bt.index, ba.index, bb.index, false); }
void FCMPUL(CPR const bt, CPR const ba, CPR const bb) { emit_XL(0xfc000000, bt.index, ba.index, bb.index, true); }
void FCTID(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc00065c, ra, rt, rb, false); }
void FCTID_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc00065c, ra, rt, rb, true); }
void FCTIDZ(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc00065e, ra, rt, rb, false); }
void FCTIDZ_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc00065e, ra, rt, rb, true); }
void FCTIW(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc00001c, ra, rt, rb, false); }
void FCTIW_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc00001c, ra, rt, rb, true); }
void FCTIWZ(CPR const bt, CPR const ba, CPR const bb) { emit_XL(0xfc00001e, bt.index, ba.index, bb.index, false); }
void FCTIWZL(CPR const bt, CPR const ba, CPR const bb) { emit_XL(0xfc00001e, bt.index, ba.index, bb.index, true); }
void FDIV(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xfc000024, frt, fra, frb, frc, false); }
void FDIV_(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xfc000024, frt, fra, frb, frc, true); }
void FDIVS(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xec000024, frt, fra, frb, frc, false); }
void FDIVS_(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xec000024, frt, fra, frb, frc, true); }
void FMADD(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xfc00003a, frt, fra, frb, frc, false); }
void FMADD_(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xfc00003a, frt, fra, frb, frc, true); }
void FMADDS(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xec00003a, frt, fra, frb, frc, false); }
void FMADDS_(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xec00003a, frt, fra, frb, frc, true); }
void FMR(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc000090, ra, rt, rb, false); }
void FMR_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc000090, ra, rt, rb, true); }
void FMSUB(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xfc000038, frt, fra, frb, frc, false); }
void FMSUB_(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xfc000038, frt, fra, frb, frc, true); }
void FMSUBS(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xec000038, frt, fra, frb, frc, false); }
void FMSUBS_(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xec000038, frt, fra, frb, frc, true); }
void FMUL(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xfc000032, frt, fra, frb, frc, false); }
void FMUL_(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xfc000032, frt, fra, frb, frc, true); }
void FMULS(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xec000032, frt, fra, frb, frc, false); }
void FMULS_(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xec000032, frt, fra, frb, frc, true); }
void FNABS(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc000110, ra, rt, rb, false); }
void FNABS_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc000110, ra, rt, rb, true); }
void FNEG(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc000050, ra, rt, rb, false); }
void FNEG_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc000050, ra, rt, rb, true); }
void FNMADD(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xfc00003e, frt, fra, frb, frc, false); }
void FNMADD_(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xfc00003e, frt, fra, frb, frc, true); }
void FNMADDS(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xec00003e, frt, fra, frb, frc, false); }
void FNMADDS_(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xec00003e, frt, fra, frb, frc, true); }
void FNMSUB(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xfc00003c, frt, fra, frb, frc, false); }
void FNMSUB_(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xfc00003c, frt, fra, frb, frc, true); }
void FNMSUBS(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xec00003c, frt, fra, frb, frc, false); }
void FNMSUBS_(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xec00003c, frt, fra, frb, frc, true); }
void FRES(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xec000030, frt, fra, frb, frc, false); }
void FRES_(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xec000030, frt, fra, frb, frc, true); }
void FRSP(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc000018, ra, rt, rb, false); }
void FRSP_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc000018, ra, rt, rb, true); }
void FRSQRTE(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xfc000034, frt, fra, frb, frc, false); }
void FRSQRTE_(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xfc000034, frt, fra, frb, frc, true); }
void FSEL(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xfc00002e, frt, fra, frb, frc, false); }
void FSEL_(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xfc00002e, frt, fra, frb, frc, true); }
void FSUB(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xfc000028, frt, fra, frb, frc, false); }
void FSUB_(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xfc000028, frt, fra, frb, frc, true); }
void FSUBS(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xec000028, frt, fra, frb, frc, false); }
void FSUBS_(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xec000028, frt, fra, frb, frc, true); }
void ICBI(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0007ac, ra, rt, rb, false); }
void ICBI_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0007ac, ra, rt, rb, true); }
void ISYNC(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x4c00012c, ra, rt, rb, false); }
void ISYNC_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x4c00012c, ra, rt, rb, true); }
void LBZ(GPR const rt, GPR const ra, uint32_t d) { emit_D(0x88000000, rt, ra, d); }
void LBZU(GPR const rt, GPR const ra, uint32_t d) { emit_D(0x8c000000, rt, ra, d); }
void LBZUX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0000ee, ra, rt, rb, false); }
void LBZUX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0000ee, ra, rt, rb, true); }
void LBZX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0000ae, ra, rt, rb, false); }
void LBZX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0000ae, ra, rt, rb, true); }
void LD(GPR const rt, GPR const ra, uint32_t d) { emit_DS(0xe8000000, rt, ra, d >> 2); }
void LDARX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0000a8, ra, rt, rb, false); }
void LDARX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0000a8, ra, rt, rb, true); }
void LDU(GPR const rt, GPR const ra, uint32_t d) { emit_DS(0xe8000001, rt, ra, d >> 2); }
void LDUX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00006a, ra, rt, rb, false); }
void LDUX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00006a, ra, rt, rb, true); }
void LDX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00002a, ra, rt, rb, false); }
void LDX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00002a, ra, rt, rb, true); }
void LFD(GPR const rt, GPR const ra, uint32_t d) { emit_D(0xc8000000, rt, ra, d); }
void LFDU(GPR const rt, GPR const ra, uint32_t d) { emit_D(0xcc000000, rt, ra, d); }
void LFDUX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0004ee, ra, rt, rb, false); }
void LFDUX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0004ee, ra, rt, rb, true); }
void LFDX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0004ae, ra, rt, rb, false); }
void LFDX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0004ae, ra, rt, rb, true); }
void LFS(GPR const rt, GPR const ra, uint32_t d) { emit_D(0xc0000000, rt, ra, d); }
void LFSU(GPR const rt, GPR const ra, uint32_t d) { emit_D(0xc4000000, rt, ra, d); }
void LFSUX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00046e, ra, rt, rb, false); }
void LFSUX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00046e, ra, rt, rb, true); }
void LFSX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00042e, ra, rt, rb, false); }
void LFSX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00042e, ra, rt, rb, true); }
void LHA(GPR const rt, GPR const ra, uint32_t d) { emit_D(0xa8000000, rt, ra, d); }
void LHAU(GPR const rt, GPR const ra, uint32_t d) { emit_D(0xac000000, rt, ra, d); }
void LHAUX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0002ee, ra, rt, rb, false); }
void LHAUX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0002ee, ra, rt, rb, true); }
void LHAX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0002ae, ra, rt, rb, false); }
void LHAX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0002ae, ra, rt, rb, true); }
void LHBRX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00062c, ra, rt, rb, false); }
void LHBRX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00062c, ra, rt, rb, true); }
void LHZ(GPR const rt, GPR const ra, uint32_t d) { emit_D(0xa0000000, rt, ra, d); }
void LHZU(GPR const rt, GPR const ra, uint32_t d) { emit_D(0xa4000000, rt, ra, d); }
void LHZUX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000296, ra, rt, rb, false); }
void LHZUX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000296, ra, rt, rb, true); }
void LHZX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00022e, ra, rt, rb, false); }
void LHZX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00022e, ra, rt, rb, true); }
void LMW(GPR const rt, GPR const ra, uint32_t d) { emit_D(0xb8000000, rt, ra, d); }
void LSWI(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0004aa, ra, rt, rb, false); }
void LSWI_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0004aa, ra, rt, rb, true); }
void LSWX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00042a, ra, rt, rb, false); }
void LSWX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00042a, ra, rt, rb, true); }
void LWA(GPR const rt, GPR const ra, uint32_t d) { emit_DS(0xe8000002, rt, ra, d >> 2); }
void LWARX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000028, ra, rt, rb, false); }
void LWARX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000028, ra, rt, rb, true); }
void LWAUX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0002ea, ra, rt, rb, false); }
void LWAUX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0002ea, ra, rt, rb, true); }
void LWAX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0002aa, ra, rt, rb, false); }
void LWAX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0002aa, ra, rt, rb, true); }
void LWBRX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00042c, ra, rt, rb, false); }
void LWBRX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00042c, ra, rt, rb, true); }
void LWZ(GPR const rt, GPR const ra, uint32_t d) { emit_D(0x80000000, rt, ra, d); }
void LWZU(GPR const rt, GPR const ra, uint32_t d) { emit_D(0x84000000, rt, ra, d); }
void LWZUX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00006e, ra, rt, rb, false); }
void LWZUX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00006e, ra, rt, rb, true); }
void LWZX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00002e, ra, rt, rb, false); }
void LWZX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00002e, ra, rt, rb, true); }
void MCRF(CPR const bt, CPR const ba, CPR const bb) { emit_XL(0x4c000000, bt.index, ba.index, bb.index, false); }
void MCRFL(CPR const bt, CPR const ba, CPR const bb) { emit_XL(0x4c000000, bt.index, ba.index, bb.index, true); }
void MCRFS(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc000080, ra, rt, rb, false); }
void MCRFS_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc000080, ra, rt, rb, true); }
void MCRXR(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000400, ra, rt, rb, false); }
void MCRXR_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000400, ra, rt, rb, true); }
void MFCR(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000026, ra, rt, rb, false); }
void MFCR_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000026, ra, rt, rb, true); }
void MFFS(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc00048e, ra, rt, rb, false); }
void MFFS_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc00048e, ra, rt, rb, true); }
void MFMSR(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0000a6, ra, rt, rb, false); }
void MFMSR_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0000a6, ra, rt, rb, true); }
void MFSPR(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0002a6, ra, rt, rb, false); }
void MFSPR_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0002a6, ra, rt, rb, true); }
void MFSR(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0004a6, ra, rt, rb, false); }
void MFSR_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0004a6, ra, rt, rb, true); }
void MFSRIN(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000526, ra, rt, rb, false); }
void MFSRIN_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000526, ra, rt, rb, true); }
void MTCRF(GPR const rt, uint32_t spr) { emit_XFX(0x7c000120, rt, spr); }
void MTFSB0(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc00008c, ra, rt, rb, false); }
void MTFSB0_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc00008c, ra, rt, rb, true); }
void MTFSB1(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc00004c, ra, rt, rb, false); }
void MTFSB1_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc00004c, ra, rt, rb, true); }
void MTFSF() { emit_XFL(0xfc00058e); }
void MTFSFI(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc00010c, ra, rt, rb, false); }
void MTFSFI_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc00010c, ra, rt, rb, true); }
void MTMSR(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000124, ra, rt, rb, false); }
void MTMSR_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000124, ra, rt, rb, true); }
void MTSPR(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0003a6, ra, rt, rb, false); }
void MTSPR_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0003a6, ra, rt, rb, true); }
void MTSR(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0001a4, ra, rt, rb, false); }
void MTSR_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0001a4, ra, rt, rb, true); }
void MTSRIN(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0001e4, ra, rt, rb, false); }
void MTSRIN_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0001e4, ra, rt, rb, true); }
void MULHD(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000092, rt, ra, rb, false, false); }
void MULHDO(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000092, rt, ra, rb, true, false); }
void MULHD_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000092, rt, ra, rb, false, true); }
void MULHDO_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000092, rt, ra, rb, true, true); }
void MULHDU(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000012, rt, ra, rb, false, false); }
void MULHDUO(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000012, rt, ra, rb, true, false); }
void MULHDU_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000012, rt, ra, rb, false, true); }
void MULHDUO_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000012, rt, ra, rb, true, true); }
void MULHW(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000096, rt, ra, rb, false, false); }
void MULHWO(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000096, rt, ra, rb, true, false); }
void MULHW_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000096, rt, ra, rb, false, true); }
void MULHWO_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000096, rt, ra, rb, true, true); }
void MULHWU(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000016, rt, ra, rb, false, false); }
void MULHWUO(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000016, rt, ra, rb, true, false); }
void MULHWU_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000016, rt, ra, rb, false, true); }
void MULHWUO_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000016, rt, ra, rb, true, true); }
void MULLD(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0001d2, rt, ra, rb, false, false); }
void MULLDO(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0001d2, rt, ra, rb, true, false); }
void MULLD_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0001d2, rt, ra, rb, false, true); }
void MULLDO_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0001d2, rt, ra, rb, true, true); }
void MULLI(GPR const rt, GPR const ra, uint32_t d) { emit_D(0x1c000000, rt, ra, d); }
void MULLW(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0001d6, rt, ra, rb, false, false); }
void MULLWO(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0001d6, rt, ra, rb, true, false); }
void MULLW_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0001d6, rt, ra, rb, false, true); }
void MULLWO_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0001d6, rt, ra, rb, true, true); }
void NAND(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0003b8, ra, rt, rb, false); }
void NAND_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0003b8, ra, rt, rb, true); }
void NEG(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0000d0, rt, ra, rb, false, false); }
void NEGO(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0000d0, rt, ra, rb, true, false); }
void NEG_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0000d0, rt, ra, rb, false, true); }
void NEGO_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0000d0, rt, ra, rb, true, true); }
void NOR(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0000f8, ra, rt, rb, false); }
void NOR_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0000f8, ra, rt, rb, true); }
void OR(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000378, ra, rt, rb, false); }
void OR_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000378, ra, rt, rb, true); }
void ORC(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000338, ra, rt, rb, false); }
void ORC_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000338, ra, rt, rb, true); }
void ORI(GPR const rt, GPR const ra, uint32_t d) { emit_D(0x60000000, ra, rt, d); }
void ORIS(GPR const rt, GPR const ra, uint32_t d) { emit_D(0x64000000, ra, rt, d); }
void RFI(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x4c000064, ra, rt, rb, false); }
void RFI_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x4c000064, ra, rt, rb, true); }
void RLDCL(GPR const rs, GPR const ra, GPR const rb, uint32_t mb) { emit_MDS(0x78000010, ra, rs, rb, mb, false); }
void RLDCL_(GPR const rs, GPR const ra, GPR const rb, uint32_t mb) { emit_MDS(0x78000010, ra, rs, rb, mb, true); }
void RLDCR(GPR const rs, GPR const ra, GPR const rb, uint32_t mb) { emit_MDS(0x78000012, ra, rs, rb, mb, false); }
void RLDCR_(GPR const rs, GPR const ra, GPR const rb, uint32_t mb) { emit_MDS(0x78000012, ra, rs, rb, mb, true); }
void RLDIC(GPR const rs, GPR const ra, uint32_t mb, uint32_t sh) { emit_MD(0x78000008, ra, rs, mb, sh, false); }
void RLDIC_(GPR const rs, GPR const ra, uint32_t mb, uint32_t sh) { emit_MD(0x78000008, ra, rs, mb, sh, true); }
void RLDICL(GPR const rs, GPR const ra, uint32_t mb, uint32_t sh) { emit_MD(0x78000000, ra, rs, mb, sh, false); }
void RLDICL_(GPR const rs, GPR const ra, uint32_t mb, uint32_t sh) { emit_MD(0x78000000, ra, rs, mb, sh, true); }
void RLDICR(GPR const rs, GPR const ra, uint32_t mb, uint32_t sh) { emit_MD(0x78000004, ra, rs, mb, sh, false); }
void RLDICR_(GPR const rs, GPR const ra, uint32_t mb, uint32_t sh) { emit_MD(0x78000004, ra, rs, mb, sh, true); }
void RLDIMI(GPR const rs, GPR const ra, uint32_t mb, uint32_t sh) { emit_MD(0x7800000c, ra, rs, mb, sh, false); }
void RLDIMI_(GPR const rs, GPR const ra, uint32_t mb, uint32_t sh) { emit_MD(0x7800000c, ra, rs, mb, sh, true); }
void RLWIMI(GPR const rs, GPR const ra, uint32_t sh, uint32_t mb, uint32_t me = 0) { emit_M(0x50000000, ra, rs, sh, mb, me, false); }
void RLWIMI_(GPR const rs, GPR const ra, uint32_t sh, uint32_t mb, uint32_t me = 0) { emit_M(0x50000000, ra, rs, sh, mb, me, true); }
void RLWINM(GPR const rs, GPR const ra, uint32_t sh, uint32_t mb, uint32_t me = 0) { emit_M(0x54000000, ra, rs, sh, mb, me, false); }
void RLWINM_(GPR const rs, GPR const ra, uint32_t sh, uint32_t mb, uint32_t me = 0) { emit_M(0x54000000, ra, rs, sh, mb, me, true); }
void RLWNM(GPR const rs, GPR const ra, GPR const rb, uint32_t mb, uint32_t me = 0) { emit_M(0x5c000000, ra, rs, rb.index, mb, me, false); }
void RLWNM_(GPR const rs, GPR const ra, GPR const rb, uint32_t mb, uint32_t me = 0) { emit_M(0x5c000000, ra, rs, rb.index, mb, me, true); }
void SC(uint32_t lev) { emit_SC(0x44000000, lev); }
void SI(GPR const rt, GPR const ra, uint32_t d) { emit_D(0x30000000, rt, ra, d); }
void SI_(GPR const rt, GPR const ra, uint32_t d) { emit_D(0x34000000, rt, ra, d); }
void SLBIA(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0003e4, ra, rt, rb, false); }
void SLBIA_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0003e4, ra, rt, rb, true); }
void SLBIE(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000364, ra, rt, rb, false); }
void SLBIE_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000364, ra, rt, rb, true); }
void SLD(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000036, ra, rt, rb, false); }
void SLD_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000036, ra, rt, rb, true); }
void SLW(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000030, ra, rt, rb, false); }
void SLW_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000030, ra, rt, rb, true); }
void SRAD(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000634, ra, rt, rb, false); }
void SRAD_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000634, ra, rt, rb, true); }
void SRADI(GPR const rt, GPR const ra, uint32_t sh) { emit_XS(0x7c000674, rt, ra, sh, false); }
void SRADI_(GPR const rt, GPR const ra, uint32_t sh) { emit_XS(0x7c000674, rt, ra, sh, true); }
void SRD(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000436, ra, rt, rb, false); }
void SRD_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000436, ra, rt, rb, true); }
void SRAW(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000630, ra, rt, rb, false); }
void SRAW_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000630, ra, rt, rb, true); }
void SRAWI(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000670, ra, rt, rb, false); }
void SRAWI_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000670, ra, rt, rb, true); }
void SRW(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000430, ra, rt, rb, false); }
void SRW_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000430, ra, rt, rb, true); }
void STB(GPR const rt, GPR const ra, uint32_t d) { emit_D(0x98000000, rt, ra, d); }
void STBU(GPR const rt, GPR const ra, uint32_t d) { emit_D(0x9c000000, rt, ra, d); }
void STBUX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0001ee, ra, rt, rb, false); }
void STBUX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0001ee, ra, rt, rb, true); }
void STBX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0001ae, ra, rt, rb, false); }
void STBX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0001ae, ra, rt, rb, true); }
void STD(GPR const rt, GPR const ra, uint32_t d) { emit_DS(0xf8000000, rt, ra, d >> 2); }
void STDCX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0001ac, ra, rt, rb, false); }
void STDCX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0001ac, ra, rt, rb, true); }
void STDU(GPR const rt, GPR const ra, uint32_t d) { emit_DS(0xf8000001, rt, ra, d >> 2); }
void STDUX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00016a, ra, rt, rb, false); }
void STDUX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00016a, ra, rt, rb, true); }
void STDX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00012a, ra, rt, rb, false); }
void STDX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00012a, ra, rt, rb, true); }
void STFD(GPR const rt, GPR const ra, uint32_t d) { emit_D(0xd8000000, rt, ra, d); }
void STFDU(GPR const rt, GPR const ra, uint32_t d) { emit_D(0xdc000000, rt, ra, d); }
void STFDUX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0005ee, ra, rt, rb, false); }
void STFDUX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0005ee, ra, rt, rb, true); }
void STFDX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0005ae, ra, rt, rb, false); }
void STFDX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0005ae, ra, rt, rb, true); }
void STFIWX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0007ae, ra, rt, rb, false); }
void STFIWX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0007ae, ra, rt, rb, true); }
void STFS(GPR const rt, GPR const ra, uint32_t d) { emit_D(0xd0000000, rt, ra, d); }
void STFSU(GPR const rt, GPR const ra, uint32_t d) { emit_D(0xd4000000, rt, ra, d); }
void STFSUX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00056e, ra, rt, rb, false); }
void STFSUX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00056e, ra, rt, rb, true); }
void STFSX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00052e, ra, rt, rb, false); }
void STFSX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00052e, ra, rt, rb, true); }
void STH(GPR const rt, GPR const ra, uint32_t d) { emit_D(0xb0000000, rt, ra, d); }
void STHBRX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00072c, ra, rt, rb, false); }
void STHBRX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00072c, ra, rt, rb, true); }
void STHU(GPR const rt, GPR const ra, uint32_t d) { emit_D(0xb4000000, rt, ra, d); }
void STHUX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00036e, ra, rt, rb, false); }
void STHUX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00036e, ra, rt, rb, true); }
void STHX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00032e, ra, rt, rb, false); }
void STHX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00032e, ra, rt, rb, true); }
void STMW(GPR const rt, GPR const ra, uint32_t d) { emit_D(0xbc000000, rt, ra, d); }
void STSWI(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0005aa, ra, rt, rb, false); }
void STSWI_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0005aa, ra, rt, rb, true); }
void STSWX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00052a, ra, rt, rb, false); }
void STSWX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00052a, ra, rt, rb, true); }
void STW(GPR const rt, GPR const ra, uint32_t d) { emit_D(0x90000000, rt, ra, d); }
void STWBRX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00052c, ra, rt, rb, false); }
void STWBRX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00052c, ra, rt, rb, true); }
void STWCX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00012c, ra, rt, rb, false); }
void STWCX__(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00012c, ra, rt, rb, true); }
void STWU(GPR const rt, GPR const ra, uint32_t d) { emit_D(0x94000000, rt, ra, d); }
void STWUX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00016e, ra, rt, rb, false); }
void STWUX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00016e, ra, rt, rb, true); }
void STWX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00012e, ra, rt, rb, false); }
void STWX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00012e, ra, rt, rb, true); }
void SUBF(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000050, rt, ra, rb, false, false); }
void SUBFO(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000050, rt, ra, rb, true, false); }
void SUBF_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000050, rt, ra, rb, false, true); }
void SUBFO_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000050, rt, ra, rb, true, true); }
void SUBFC(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000010, rt, ra, rb, false, false); }
void SUBFCO(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000010, rt, ra, rb, true, false); }
void SUBFC_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000010, rt, ra, rb, false, true); }
void SUBFCO_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000010, rt, ra, rb, true, true); }
void SUBFE(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000110, rt, ra, rb, false, false); }
void SUBFEO(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000110, rt, ra, rb, true, false); }
void SUBFE_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000110, rt, ra, rb, false, true); }
void SUBFEO_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000110, rt, ra, rb, true, true); }
void SUBFIC(GPR const rt, GPR const ra, uint32_t d) { emit_D(0x20000000, rt, ra, d); }
void SUBFME(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0001d0, rt, ra, rb, false, false); }
void SUBFMEO(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0001d0, rt, ra, rb, true, false); }
void SUBFME_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0001d0, rt, ra, rb, false, true); }
void SUBFMEO_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0001d0, rt, ra, rb, true, true); }
void SUBFZE(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000190, rt, ra, rb, false, false); }
void SUBFZEO(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000190, rt, ra, rb, true, false); }
void SUBFZE_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000190, rt, ra, rb, false, true); }
void SUBFZEO_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000190, rt, ra, rb, true, true); }
void SYNC(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0004ac, ra, rt, rb, false); }
void SYNC_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0004ac, ra, rt, rb, true); }
void TD(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000088, ra, rt, rb, false); }
void TD_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000088, ra, rt, rb, true); }
void TDI(GPR const rt, GPR const ra, uint32_t d) { emit_D(0x08000000, rt, ra, d); }
void TLBIE(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000264, ra, rt, rb, false); }
void TLBIE_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000264, ra, rt, rb, true); }
void TLBSYNC(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00046c, ra, rt, rb, false); }
void TLBSYNC_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00046c, ra, rt, rb, true); }
void TW(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000008, ra, rt, rb, false); }
void TW_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000008, ra, rt, rb, true); }
void TWI(GPR const rt, GPR const ra, uint32_t d) { emit_D(0x0c000000, rt, ra, d); }
void XOR(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000278, ra, rt, rb, false); }
void XOR_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000278, ra, rt, rb, true); }
void XORI(GPR const rt, GPR const ra, uint32_t d) { emit_D(0x68000000, ra, rt, d); }
void XORIS(GPR const rt, GPR const ra, uint32_t d) { emit_D(0x6c000000, ra, rt, d); }
+340
View File
@@ -0,0 +1,340 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include <catch2/catch_test_macros.hpp>
#include <sys/mman.h>
#include "powah_emit.hpp"
#define EB32(X) __bswap32(X)
TEST_CASE("ppc64: addi", "[ppc64]") {
std::vector<uint32_t> data(64);
powah::Context ctx(data.data(), data.size());
ctx.ADDI(powah::R2, powah::R2, 0);
ctx.ADDIS(powah::R2, powah::R12, 0);
REQUIRE(data[0] == EB32(0x00004238));
REQUIRE(data[1] == EB32(0x00004c3c));
}
TEST_CASE("ppc64: std/mr", "[ppc64]") {
std::vector<uint32_t> data(64);
powah::Context ctx(data.data(), data.size());
ctx.STD(powah::R26, powah::R1, 144);
ctx.MR(powah::R26, powah::R4);
ctx.STD(powah::R30, powah::R1, 176);
ctx.MR(powah::R30, powah::R3);
ctx.RLDICR(powah::R4, powah::R5, 25, 6);
ctx.STD(powah::R28, powah::R1, 160);
REQUIRE(data[0] == EB32(0x900041fb));
REQUIRE(data[1] == EB32(0x78239a7c));
REQUIRE(data[2] == EB32(0xb000c1fb));
REQUIRE(data[3] == EB32(0x781b7e7c));
REQUIRE(data[4] == EB32(0x84c9a478));
REQUIRE(data[5] == EB32(0xa00081fb));
}
TEST_CASE("ppc64: sldi", "[ppc64]") {
std::vector<uint32_t> data(64);
powah::Context ctx(data.data(), data.size());
ctx.SLDI(powah::R3, powah::R5, 37);
REQUIRE(data[0] == EB32(0x862ea378));
}
TEST_CASE("ppc64: rldicr", "[ppc64]") {
std::vector<uint32_t> data(64);
powah::Context ctx(data.data(), data.size());
ctx.RLDICR(powah::R1, powah::R1, 0, 0);
ctx.RLDICR(powah::R1, powah::R1, 1, 0);
ctx.RLDICR(powah::R1, powah::R1, 0, 1);
ctx.RLDICR(powah::R1, powah::R1, 1, 1);
ctx.RLDICR(powah::R1, powah::R1, 31, 31);
REQUIRE(data[0] == EB32(0x04002178));
REQUIRE(data[1] == EB32(0x04082178));
REQUIRE(data[2] == EB32(0x44002178));
REQUIRE(data[3] == EB32(0x44082178));
REQUIRE(data[4] == EB32(0xc4ff2178));
}
TEST_CASE("ppc64: mr/or/std", "[ppc64]") {
std::vector<uint32_t> data(64);
powah::Context ctx(data.data(), data.size());
ctx.MR(powah::R27, powah::R7);
ctx.MR(powah::R29, powah::R6);
ctx.OR(powah::R3, powah::R3, powah::R4);
ctx.MR(powah::R4, powah::R28);
ctx.CRMOVE(powah::CPR{8}, powah::CPR{1});
ctx.MR(powah::R25, powah::R5);
ctx.OR(powah::R3, powah::R3, powah::R26);
ctx.STD(powah::R3, powah::R1, 56);
ctx.MR(powah::R3, powah::R30);
REQUIRE(data[0] == EB32(0x783bfb7c));
REQUIRE(data[1] == EB32(0x7833dd7c));
REQUIRE(data[2] == EB32(0x7823637c));
REQUIRE(data[3] == EB32(0x78e3847f));
REQUIRE(data[4] == EB32(0x820b014d));
REQUIRE(data[5] == EB32(0x782bb97c));
REQUIRE(data[6] == EB32(0x78d3637c));
REQUIRE(data[7] == EB32(0x380061f8));
REQUIRE(data[8] == EB32(0x78f3c37f));
}
TEST_CASE("ppc64: ld/crand+addi", "[ppc64]") {
std::vector<uint32_t> data(64);
powah::Context ctx(data.data(), data.size());
ctx.LD(powah::R4, powah::R1, 72);
ctx.LWZ(powah::R5, powah::R1, 80);
ctx.CRAND(powah::CPR{20}, powah::CPR{10}, powah::CPR{9});
ctx.ADDI(powah::R6, powah::R4, 4);
ctx.ANDIS_(powah::R4, powah::R5, 1992);
ctx.LD(powah::R5, powah::R30, 1984);
ctx.LBZ(powah::R3, powah::R1, 84);
ctx.CLRLDI(powah::R26, powah::R6, 8);
ctx.STW(powah::R4, powah::R1, 80);
ctx.ADDI(powah::R5, powah::R5, 1);
ctx.STD(powah::R26, powah::R1, 72);
ctx.STD(powah::R5, powah::R30, 1984);
REQUIRE(data[0] == EB32(0x480081e8));
REQUIRE(data[1] == EB32(0x5000a180));
REQUIRE(data[2] == EB32(0x024a8a4e));
REQUIRE(data[3] == EB32(0x0400c438));
REQUIRE(data[4] == EB32(0xc807a474));
REQUIRE(data[5] == EB32(0xc007bee8));
REQUIRE(data[6] == EB32(0x54006188));
REQUIRE(data[7] == EB32(0x0002da78));
REQUIRE(data[8] == EB32(0x50008190));
REQUIRE(data[9] == EB32(0x0100a538));
REQUIRE(data[10] == EB32(0x480041fb));
REQUIRE(data[11] == EB32(0xc007bef8));
}
TEST_CASE("ppc64: rotldi", "[ppc64]") {
std::vector<uint32_t> data(64);
powah::Context ctx(data.data(), data.size());
ctx.ROTLDI(powah::R5, powah::R3, 16);
ctx.ROTLDI(powah::R4, powah::R3, 8);
ctx.RLDIMI(powah::R4, powah::R5, 8, 48);
ctx.ROTLDI(powah::R5, powah::R3, 24);
ctx.RLDIMI(powah::R4, powah::R5, 16, 40);
ctx.ROTLDI(powah::R5, powah::R3, 32);
ctx.RLDIMI(powah::R4, powah::R5, 24, 32);
ctx.ROTLDI(powah::R5, powah::R3, 48);
ctx.RLDIMI(powah::R4, powah::R5, 40, 16);
ctx.ROTLDI(powah::R5, powah::R3, 56);
ctx.RLDIMI(powah::R4, powah::R5, 48, 8);
ctx.RLDIMI(powah::R4, powah::R3, 56, 0);
ctx.MR(powah::R3, powah::R4);
REQUIRE(data[0] == EB32(0x00806578));
REQUIRE(data[1] == EB32(0x00406478));
REQUIRE(data[2] == EB32(0x2c44a478));
REQUIRE(data[3] == EB32(0x00c06578));
REQUIRE(data[4] == EB32(0x2c82a478));
REQUIRE(data[5] == EB32(0x02006578));
REQUIRE(data[6] == EB32(0x2cc0a478));
REQUIRE(data[7] == EB32(0x02806578));
REQUIRE(data[8] == EB32(0x0e44a478));
REQUIRE(data[9] == EB32(0x02c06578));
REQUIRE(data[10] == EB32(0x0e82a478));
REQUIRE(data[11] == EB32(0x0ec06478));
REQUIRE(data[12] == EB32(0x7823837c));
}
TEST_CASE("ppc64: branch-backwards", "[ppc64]") {
std::vector<uint32_t> data(64);
powah::Context ctx(data.data(), data.size());
powah::Label l_3 = ctx.DefineLabel();
ctx.LABEL(l_3);
ctx.ADD(powah::R1, powah::R2, powah::R3);
ctx.B(l_3);
ctx.ApplyRelocs();
REQUIRE(data[0] == EB32(0x141a227c));
REQUIRE(data[1] == EB32(0xfcffff4b));
}
TEST_CASE("ppc64: rlwimi madness", "[ppc64]") {
std::vector<uint32_t> data(64);
powah::Context ctx(data.data(), data.size());
ctx.ROTLWI(powah::R10, powah::R3, 24);
ctx.SRDI(powah::R9, powah::R3, 32);
ctx.RLWIMI(powah::R10, powah::R3, 8, 8, 15);
ctx.RLWIMI(powah::R10, powah::R3, 8, 24, 31);
ctx.ROTLWI(powah::R3, powah::R9, 24);
ctx.RLWIMI(powah::R3, powah::R9, 8, 8, 15);
ctx.RLWIMI(powah::R3, powah::R9, 8, 24, 31);
ctx.RLDIMI(powah::R3, powah::R10, 32, 0);
REQUIRE(data[0] == EB32(0x3ec06a54));
REQUIRE(data[1] == EB32(0x22006978));
REQUIRE(data[2] == EB32(0x1e426a50));
REQUIRE(data[3] == EB32(0x3e466a50));
REQUIRE(data[4] == EB32(0x3ec02355));
REQUIRE(data[5] == EB32(0x1e422351));
REQUIRE(data[6] == EB32(0x3e462351));
REQUIRE(data[7] == EB32(0x0e004379));
}
/*
0: 78 1b 68 7c mr 8, 3
4: 38 28 83 7c and 3, 4, 5
8: 74 00 6a 7c cntlzd 10, 3
c: 76 06 69 7c sradi 9, 3, 32
10: 82 d1 4a 79 rldicl 10, 10, 58, 6
14: 00 00 29 55 rlwinm 9, 9, 0, 0, 0
18: 64 f0 4a 79 sldi 10, 10, 30
1c: 78 53 29 7d or 9, 9, 10
20: 00 00 28 f9 std 9, 0(8)
24: 20 00 80 4e blr
*/
TEST_CASE("ppc64: functor-2", "[ppc64]") {
std::vector<uint32_t> data(64);
powah::Context ctx(data.data(), data.size());
ctx.MR(powah::R8, powah::R3);
ctx.AND(powah::R3, powah::R4, powah::R5);
ctx.CNTLZD(powah::R10, powah::R3);
ctx.SRADI(powah::R9, powah::R3, 32);
ctx.RLDICL(powah::R10, powah::R10, 58, 6);
ctx.RLWINM(powah::R9, powah::R9, 0, 0, 0);
ctx.SLDI(powah::R10, powah::R10, 30);
ctx.OR(powah::R9, powah::R9, powah::R10);
ctx.STD(powah::R9, powah::R8, 0);
ctx.BLR();
REQUIRE(data[0] == EB32(0x781b687c));
REQUIRE(data[1] == EB32(0x3828837c));
REQUIRE(data[2] == EB32(0x74006a7c));
REQUIRE(data[3] == EB32(0x7606697c));
REQUIRE(data[4] == EB32(0x82d14a79));
REQUIRE(data[5] == EB32(0x00002955));
REQUIRE(data[6] == EB32(0x64f04a79));
REQUIRE(data[7] == EB32(0x7853297d));
REQUIRE(data[8] == EB32(0x000028f9));
REQUIRE(data[9] == EB32(0x2000804e));
}
TEST_CASE("ppc64: functor-1", "[ppc64]") {
std::vector<uint32_t> data(64);
powah::Context ctx(data.data(), data.size());
powah::Label l_4 = ctx.DefineLabel();
powah::Label l_7 = ctx.DefineLabel();
ctx.CMPLD(powah::CR0, powah::R3, powah::R4);
ctx.MR(powah::R9, powah::R3);
ctx.BGT(powah::CR0, l_7);
ctx.CMPLDI(powah::CR0, powah::R3, 3781);
ctx.LI(powah::R3, 1);
ctx.BGTLR(powah::CR0);
ctx.CMPLD(powah::CR0, powah::R9, powah::R5);
ctx.BLE(powah::CR0, l_4);
ctx.SUBF(powah::R5, powah::R9, powah::R5);
ctx.XOR(powah::R3, powah::R5, powah::R4);
ctx.BLR();
ctx.LABEL(l_4);
ctx.ADD(powah::R5, powah::R4, powah::R5);
ctx.ADD(powah::R3, powah::R5, powah::R9);
ctx.BLR();
ctx.LABEL(l_7);
ctx.ADD(powah::R3, powah::R4, powah::R5);
ctx.BLR();
ctx.ApplyRelocs();
REQUIRE(data[0] == EB32(0x4020237c));
REQUIRE(data[1] == EB32(0x781b697c));
REQUIRE(data[2] == EB32(0x30008141));
REQUIRE(data[3] == EB32(0xc50e2328));
REQUIRE(data[4] == EB32(0x01006038));
REQUIRE(data[5] == EB32(0x2000814d));
REQUIRE(data[6] == EB32(0x4028297c));
REQUIRE(data[7] == EB32(0x10008140));
REQUIRE(data[8] == EB32(0x5028a97c));
REQUIRE(data[9] == EB32(0x7822a37c));
REQUIRE(data[10] == EB32(0x2000804e));
REQUIRE(data[11] == EB32(0x142aa47c));
REQUIRE(data[12] == EB32(0x144a657c));
REQUIRE(data[13] == EB32(0x2000804e));
REQUIRE(data[14] == EB32(0x142a647c));
REQUIRE(data[15] == EB32(0x2000804e));
}
#if defined(ARCHITECTURE_powerpc64)
/*
0: d619637c mullw 3, 3, 3
4: 20006378 clrldi 3, 3, 32
8: 2000804e blr
*/
TEST_CASE("ppc64: live-exec square", "[ppc64]") {
uint32_t* data = (uint32_t*)mmap(nullptr, 4096, PROT_WRITE | PROT_READ | PROT_EXEC, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
REQUIRE(data != nullptr);
powah::Context ctx((void*)data, 4096);
ctx.MULLW(powah::R3, powah::R3, powah::R3);
ctx.CLRLDI(powah::R3, powah::R3, 32);
ctx.BLR();
REQUIRE(data[0] == EB32(0xd619637c));
REQUIRE(data[1] == EB32(0x20006378));
REQUIRE(data[2] == EB32(0x2000804e));
auto* fn = (int (*)(int))data;
REQUIRE(fn(4) == 4 * 4);
REQUIRE(fn(8) == 8 * 8);
munmap((void*)data, 4096);
}
/*
int f(int a, int b, int c) {
a += (a > b) ? b - 0xffff : c + 402, b += 2, c &= b*c;
return a * a + b ^ c & b;
}
*/
TEST_CASE("ppc64: live-exec xoralu", "[ppc64]") {
uint32_t* data = (uint32_t*)mmap(nullptr, 4096, PROT_WRITE | PROT_READ | PROT_EXEC, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
REQUIRE(data != nullptr);
powah::Context ctx((void*)data, 4096);
powah::Label l_2 = ctx.DefineLabel();
powah::Label l_3 = ctx.DefineLabel();
ctx.CMPW(powah::R3, powah::R4);
ctx.BGT(powah::CR0, l_2);
ctx.ADDI(powah::R6, powah::R5, 402);
ctx.B(l_3);
ctx.LABEL(l_2);
ctx.ADDI(powah::R6, powah::R4, 1);
ctx.ADDIS(powah::R6, powah::R6, -1);
ctx.LABEL(l_3);
ctx.ADDI(powah::R4, powah::R4, 2);
ctx.ADD(powah::R3, powah::R6, powah::R3);
ctx.MULLW(powah::R6, powah::R4, powah::R5);
ctx.MULLW(powah::R3, powah::R3, powah::R3);
ctx.AND(powah::R5, powah::R5, powah::R6);
ctx.ADD(powah::R3, powah::R3, powah::R4);
ctx.AND(powah::R4, powah::R5, powah::R4);
ctx.XOR(powah::R3, powah::R3, powah::R4);
ctx.EXTSW(powah::R3, powah::R3);
ctx.BLR();
ctx.ApplyRelocs();
REQUIRE(data[0] == EB32(0x0020037c));
REQUIRE(data[1] == EB32(0x0c008141));
REQUIRE(data[2] == EB32(0x9201c538));
REQUIRE(data[3] == EB32(0x0c000048));
REQUIRE(data[4] == EB32(0x0100c438));
REQUIRE(data[5] == EB32(0xffffc63c));
REQUIRE(data[6] == EB32(0x02008438));
REQUIRE(data[7] == EB32(0x141a667c));
REQUIRE(data[8] == EB32(0xd629c47c));
REQUIRE(data[9] == EB32(0xd619637c));
REQUIRE(data[10] == EB32(0x3830a57c));
REQUIRE(data[11] == EB32(0x1422637c));
REQUIRE(data[12] == EB32(0x3820a47c));
REQUIRE(data[13] == EB32(0x7822637c));
REQUIRE(data[14] == EB32(0xb407637c));
REQUIRE(data[15] == EB32(0x2000804e));
auto const orig = [](int a, int b, int c) {
a += (a > b) ? b - 0xffff : c + 402, b += 2, c &= b*c;
return a * a + b ^ c & b;
};
auto* fn = (int (*)(int, int, int))data;
REQUIRE(fn(0, 1, 2) == orig(0, 1, 2));
REQUIRE(fn(6456, 4564, 4564561) == orig(6456, 4564, 4564561));
munmap((void*)data, 4096);
}
#endif
+1 -1
View File
@@ -8,7 +8,7 @@
include_directories(.) include_directories(.)
# Dynarmic # 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_subdirectory(dynarmic)
add_library(dynarmic::dynarmic ALIAS dynarmic) add_library(dynarmic::dynarmic ALIAS dynarmic)
endif() endif()
@@ -114,9 +114,7 @@ class QuickSettings(val emulationFragment: EmulationFragment) {
name: Int, name: Int,
container: ViewGroup, container: ViewGroup,
setting: BooleanSetting, setting: BooleanSetting
isEnabled: Boolean = true,
onValueChanged: ((Boolean) -> Unit)? = null
) { ) {
val inflater = LayoutInflater.from(emulationFragment.requireContext()) val inflater = LayoutInflater.from(emulationFragment.requireContext())
val itemView = inflater.inflate(R.layout.item_quick_settings_menu, container, false) val itemView = inflater.inflate(R.layout.item_quick_settings_menu, container, false)
@@ -127,22 +125,16 @@ class QuickSettings(val emulationFragment: EmulationFragment) {
titleView.text = YuzuApplication.appContext.getString(name) titleView.text = YuzuApplication.appContext.getString(name)
switchContainer.visibility = View.VISIBLE switchContainer.visibility = View.VISIBLE
switchContainer.isEnabled = isEnabled
switchView.isChecked = setting.getBoolean() switchView.isChecked = setting.getBoolean()
switchView.isEnabled = isEnabled
itemView.alpha = if (isEnabled) 1.0f else 0.5f
switchView.setOnCheckedChangeListener { _, isChecked -> switchView.setOnCheckedChangeListener { _, isChecked ->
setting.setBoolean(isChecked) setting.setBoolean(isChecked)
saveSettings() saveSettings()
onValueChanged?.invoke(isChecked)
} }
switchContainer.setOnClickListener { switchContainer.setOnClickListener {
if (switchView.isEnabled) {
switchView.toggle() switchView.toggle()
} }
}
container.addView(itemView) container.addView(itemView)
} }
@@ -185,9 +177,7 @@ class QuickSettings(val emulationFragment: EmulationFragment) {
setting: AbstractSetting, setting: AbstractSetting,
minValue: Int = 0, minValue: Int = 0,
maxValue: Int = 100, maxValue: Int = 100,
units: String = "", units: String = ""
isEnabled: Boolean = true,
onValueChanged: ((Int) -> Unit)? = null
) { ) {
val inflater = LayoutInflater.from(emulationFragment.requireContext()) val inflater = LayoutInflater.from(emulationFragment.requireContext())
val itemView = inflater.inflate(R.layout.item_quick_settings_menu, container, false) val itemView = inflater.inflate(R.layout.item_quick_settings_menu, container, false)
@@ -200,13 +190,10 @@ class QuickSettings(val emulationFragment: EmulationFragment) {
titleView.text = YuzuApplication.appContext.getString(name) titleView.text = YuzuApplication.appContext.getString(name)
sliderContainer.visibility = View.VISIBLE sliderContainer.visibility = View.VISIBLE
sliderContainer.isEnabled = isEnabled
slider.valueFrom = minValue.toFloat() slider.valueFrom = minValue.toFloat()
slider.valueTo = maxValue.toFloat() slider.valueTo = maxValue.toFloat()
slider.stepSize = 1f slider.stepSize = 1f
slider.isEnabled = isEnabled
itemView.alpha = if (isEnabled) 1.0f else 0.5f
val currentValue = when (setting) { val currentValue = when (setting) {
is AbstractShortSetting -> setting.getShort(needsGlobal = false).toInt() is AbstractShortSetting -> setting.getShort(needsGlobal = false).toInt()
is AbstractIntSetting -> setting.getInt(needsGlobal = false) is AbstractIntSetting -> setting.getInt(needsGlobal = false)
@@ -217,8 +204,8 @@ class QuickSettings(val emulationFragment: EmulationFragment) {
val displayValue = "${slider.value.toInt()}$units" val displayValue = "${slider.value.toInt()}$units"
valueDisplay.text = displayValue valueDisplay.text = displayValue
slider.addOnChangeListener { _, value, changed -> slider.addOnChangeListener { _, value, chanhed ->
if (changed) { if (chanhed) {
val intValue = value.toInt() val intValue = value.toInt()
when (setting) { when (setting) {
is AbstractShortSetting -> setting.setShort(intValue.toShort()) is AbstractShortSetting -> setting.setShort(intValue.toShort())
@@ -226,7 +213,6 @@ class QuickSettings(val emulationFragment: EmulationFragment) {
} }
saveSettings() saveSettings()
valueDisplay.text = "$intValue$units" valueDisplay.text = "$intValue$units"
onValueChanged?.invoke(intValue)
} }
} }
@@ -92,7 +92,6 @@ import org.yuzu.yuzu_emu.utils.GameIconUtils
import org.yuzu.yuzu_emu.utils.GpuDriverHelper import org.yuzu.yuzu_emu.utils.GpuDriverHelper
import org.yuzu.yuzu_emu.utils.InputHandler import org.yuzu.yuzu_emu.utils.InputHandler
import org.yuzu.yuzu_emu.utils.Log import org.yuzu.yuzu_emu.utils.Log
import org.yuzu.yuzu_emu.utils.LosslessScalingHelper
import org.yuzu.yuzu_emu.utils.NativeConfig import org.yuzu.yuzu_emu.utils.NativeConfig
import org.yuzu.yuzu_emu.utils.NativeFreedrenoConfig import org.yuzu.yuzu_emu.utils.NativeFreedrenoConfig
import org.yuzu.yuzu_emu.utils.ViewUtils import org.yuzu.yuzu_emu.utils.ViewUtils
@@ -1158,36 +1157,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
quickSettings.addDivider(container) quickSettings.addDivider(container)
val frameGenAvailable = LosslessScalingHelper.isInstalled() &&
LosslessScalingHelper.isSupportedByGpu()
val frameGenEnabled = BooleanSetting.RENDERER_FRAME_GEN.getBoolean()
val usesFixedMultiplier =
IntSetting.RENDERER_FRAME_GEN_TARGET_RATE.getInt() == 0
quickSettings.addBooleanSetting(
R.string.frame_gen,
container,
BooleanSetting.RENDERER_FRAME_GEN,
isEnabled = frameGenAvailable
) {
NativeLibrary.applySettings()
addQuickSettings()
}
quickSettings.addSliderSetting(
R.string.frame_gen_multiplier,
container,
IntSetting.RENDERER_FRAME_GEN_MULTIPLIER,
minValue = 2,
maxValue = 4,
units = "x",
isEnabled = frameGenAvailable && frameGenEnabled && usesFixedMultiplier
) {
NativeLibrary.applySettings()
}
quickSettings.addDivider(container)
quickSettings.addIntSetting( quickSettings.addIntSetting(
R.string.renderer_accuracy, R.string.renderer_accuracy,
container, container,
@@ -22,7 +22,6 @@
#include "input_common/main.h" #include "input_common/main.h"
#include "jni/emu_window/emu_window.h" #include "jni/emu_window/emu_window.h"
#include "jni/native.h" #include "jni/native.h"
#include "video_core/renderer_base.h"
void EmuWindow_Android::OnSurfaceChanged(ANativeWindow* surface) { void EmuWindow_Android::OnSurfaceChanged(ANativeWindow* surface) {
if (!surface) { if (!surface) {
@@ -126,14 +125,10 @@ float EmuWindow_Android::GetFrameTimeVerifiedHint() const {
} }
float EmuWindow_Android::GetPresentedFrameMultiplier() { float EmuWindow_Android::GetPresentedFrameMultiplier() {
if (EmulationSession::GetInstance().IsRunning()) { if (!Settings::values.frame_gen.GetValue()) {
const VideoCore::FrameGenConfig config = return 1.0f;
EmulationSession::GetInstance().System().Renderer().Settings().GetFrameGenConfig();
return config.enabled ? static_cast<float>(config.multiplier) : 1.0f;
} }
return Settings::values.frame_gen.GetValue() return static_cast<float>(std::clamp<u32>(Settings::values.frame_gen_multiplier.GetValue(), 2, 4));
? static_cast<float>(Settings::FrameGenMultiplier())
: 1.0f;
} }
float EmuWindow_Android::GetFrameRateHint() const { float EmuWindow_Android::GetFrameRateHint() const {
@@ -3,7 +3,7 @@
<!-- General application strings --> <!-- General application strings -->
<string name="app_name" translatable="false">Eden</string> <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.&lt;br /&gt;&lt;br /&gt;Before you begin, please locate your <![CDATA[<b> prod.keys </b>]]> file on your device storage.&lt;br /&gt;&lt;br /&gt;<![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.&lt;br /&gt;&lt;br /&gt;Before you begin, please locate your <![CDATA[<b> prod.keys </b>]]> file on your device storage.&lt;br /&gt;&lt;br /&gt;<![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_name">Notices and errors</string>
<string name="notice_notification_channel_id" translatable="false">noticesAndErrors</string> <string name="notice_notification_channel_id" translatable="false">noticesAndErrors</string>
<string name="notice_notification_channel_description">Shows notifications when something goes wrong.</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_error_file_not_found">ROM file does not exist</string>
<string name="loader_requires_firmware">Game Requires Firmware</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 --> <!-- Intent Launch strings -->
<string name="searching_for_game">Searching for game...</string> <string name="searching_for_game">Searching for game...</string>
+2 -3
View File
@@ -89,7 +89,6 @@ add_library(
param_package.h param_package.h
parent_of_member.h parent_of_member.h
point.h point.h
quaternion.h
range_map.h range_map.h
range_mutex.h range_mutex.h
range_sets.h range_sets.h
@@ -109,6 +108,8 @@ add_library(
settings_setting.h settings_setting.h
slot_vector.h slot_vector.h
socket_types.h socket_types.h
sparse_large_vector.cpp
sparse_large_vector.h
spin_lock.h spin_lock.h
stb.cpp stb.cpp
stb.h stb.h
@@ -138,8 +139,6 @@ add_library(
uuid.cpp uuid.cpp
uuid.h uuid.h
vector_math.h vector_math.h
virtual_buffer.cpp
virtual_buffer.h
zstd_compression.cpp zstd_compression.cpp
zstd_compression.h zstd_compression.h
fs/ryujinx_compat.h fs/ryujinx_compat.cpp fs/ryujinx_compat.h fs/ryujinx_compat.cpp
-1
View File
@@ -9,7 +9,6 @@
#include "common/assert.h" #include "common/assert.h"
#include "common/fiber.h" #include "common/fiber.h"
#include "common/virtual_buffer.h"
#include <boost/context/detail/fcontext.hpp> #include <boost/context/detail/fcontext.hpp>
+40 -13
View File
@@ -117,7 +117,6 @@ template <typename T>
static void GetFuncAddress(Common::DynamicLibrary& dll, const char* name, T& pfn) { static void GetFuncAddress(Common::DynamicLibrary& dll, const char* name, T& pfn) {
if (!dll.GetSymbol(name, &pfn)) { if (!dll.GetSymbol(name, &pfn)) {
LOG_CRITICAL(HW_Memory, "Failed to load {}", name); LOG_CRITICAL(HW_Memory, "Failed to load {}", name);
throw std::bad_alloc{};
} }
} }
@@ -179,6 +178,14 @@ public:
Release(); 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) { void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perms) {
std::unique_lock lock{placeholder_mutex}; std::unique_lock lock{placeholder_mutex};
if (!IsNiechePlaceholder(virtual_offset, length)) { if (!IsNiechePlaceholder(virtual_offset, length)) {
@@ -399,6 +406,10 @@ private:
// For managarm: see https://github.com/managarm/managarm/issues/1370 // For managarm: see https://github.com/managarm/managarm/issues/1370
#else // ^^^ Windows ^^^ vvv POSIX vvv #else // ^^^ Windows ^^^ vvv POSIX vvv
#ifndef MAP_NOCORE
#define MAP_NOCORE 0
#endif
#ifdef ARCHITECTURE_arm64 #ifdef ARCHITECTURE_arm64
static void* ChooseVirtualBase(size_t virtual_size) { 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. // Note: we may be able to take advantage of MAP_FIXED_NOREPLACE here.
void* map_pointer = void* map_pointer =
mmap(reinterpret_cast<void*>(hint_address), virtual_size, PROT_READ | PROT_WRITE, 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 we successfully mapped, we're done.
if (reinterpret_cast<uintptr_t>(map_pointer) == hint_address) { 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) { static void* ChooseVirtualBase(size_t virtual_size) {
#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__OpenBSD__) || defined(__sun__) || defined(__HAIKU__) || defined(__managarm__) || defined(__AIX__) #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) if (virtual_base != MAP_FAILED)
return virtual_base; return virtual_base;
#endif #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 #endif
@@ -541,13 +552,13 @@ public:
} }
if (use_anon) { if (use_anon) {
LOG_WARNING(Common_Memory, "Using private mappings instead of shared ones"); 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) { if (fd > 0) {
fd = -1; fd = -1;
close(fd); close(fd);
} }
} else { } 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) { if (backing_base == MAP_FAILED) {
LOG_CRITICAL(HW_Memory, "mmap failed: {}", strerror(errno)); LOG_CRITICAL(HW_Memory, "mmap failed: {}", strerror(errno));
@@ -571,6 +582,14 @@ public:
Release(); 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) { void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perms) {
// Intersect the range with our address space. // Intersect the range with our address space.
AdjustMap(&virtual_offset, &length); AdjustMap(&virtual_offset, &length);
@@ -691,12 +710,10 @@ HostMemory::HostMemory(size_t backing_size_, size_t virtual_size_)
{ {
#if defined(__OPENORBIS__) || defined(__managarm__) #if defined(__OPENORBIS__) || defined(__managarm__)
LOG_WARNING(HW_Memory, "Platform doesn't support fastmem"); LOG_WARNING(HW_Memory, "Platform doesn't support fastmem");
fallback_buffer.emplace(backing_size); backing_base = static_cast<u8*>(mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
backing_base = fallback_buffer->data();
virtual_base = nullptr; virtual_base = nullptr;
#else #else
// Try to allocate a fastmem arena. // 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); impl = std::make_unique<HostMemory::Impl>(AlignUp(backing_size, PageAlignment), AlignUp(virtual_size, PageAlignment) + HugePageSize);
if (impl->Init()) { if (impl->Init()) {
backing_base = impl->backing_base; 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; virtual_base_offset = virtual_base - impl->virtual_base;
} }
} else { } else {
impl.reset();
LOG_WARNING(HW_Memory, "Platform can support fastmem, but can't create it"); LOG_WARNING(HW_Memory, "Platform can support fastmem, but can't create it");
fallback_buffer.emplace(backing_size); fallback_buffer = true;
backing_base = fallback_buffer->data(); backing_base = static_cast<u8*>(impl->Allocate(backing_size));
virtual_base = nullptr; virtual_base = nullptr;
impl.reset();
} }
#endif #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; HostMemory::HostMemory(HostMemory&&) noexcept = default;
+1 -2
View File
@@ -10,7 +10,6 @@
#include <optional> #include <optional>
#include "common/common_funcs.h" #include "common/common_funcs.h"
#include "common/common_types.h" #include "common/common_types.h"
#include "common/virtual_buffer.h"
namespace Common { namespace Common {
@@ -86,7 +85,7 @@ private:
u8* virtual_base{}; u8* virtual_base{};
size_t virtual_base_offset{}; size_t virtual_base_offset{};
// Windows requires it for kernels whom lack proper support for some functions! // 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 } // namespace Common
+5 -33
View File
@@ -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-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
@@ -13,39 +13,11 @@ PageTable::PageTable() = default;
PageTable::~PageTable() noexcept = default; PageTable::~PageTable() noexcept = default;
bool PageTable::BeginTraversal(TraversalEntry* out_entry, TraversalContext* out_context, void PageTable::Resize(std::size_t address_space_width_in_bits, std::size_t page_bits) {
Common::ProcessAddress address) const { auto const num_page_table_entries = 1ULL << (address_space_width_in_bits - page_bits);
out_context->next_offset = GetInteger(address); entries.ResizeAndClear(num_page_table_entries);
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);
current_address_space_width_in_bits = address_space_width_in_bits; 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 } // namespace Common
+64 -56
View File
@@ -9,22 +9,22 @@
#include <atomic> #include <atomic>
#include "common/common_types.h" #include "common/common_types.h"
#include "common/sparse_large_vector.h"
#include "common/typed_address.h" #include "common/typed_address.h"
#include "common/virtual_buffer.h"
namespace Common { namespace Common {
enum class PageType : u8 { enum class PageType : u8 {
/// Page is unmapped and should cause an access error. /// 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. /// 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 /// Page is mapped to regular memory, but inaccessible from CPU fastmem and must use
/// the callbacks. /// the callbacks.
DebugMemory, DebugMemory = 0b10,
/// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and /// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and
/// invalidation /// invalidation
RasterizerCachedMemory, RasterizerCachedMemory = 0b11,
}; };
/** /**
@@ -42,57 +42,86 @@ struct PageTable {
u64 next_offset{}; u64 next_offset{};
}; };
/// Number of bits reserved for attribute tagging. /// Masks out bits reserved for attribute tagging.
/// This can be at most the guaranteed alignment of the pointers in the page table. static constexpr u64 ATTRIBUTE_MASK = ((1ULL << 44) - 1) << 12;
static constexpr int ATTRIBUTE_BITS = 2;
/// 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. * Atomic tuple of host pointer, page type, and block id.
* This uses the lower bits of a given pointer to store the attribute tag. * 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 * 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. * call. In other words, they are guaranteed to be synchronized at all times.
*/ */
class PageInfo { class PageEntryData {
public: 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 /// Returns the page pointer
[[nodiscard]] uintptr_t Pointer() const noexcept { [[nodiscard]] uintptr_t Pointer(bool ignored_marked = false) const noexcept {
return ExtractPointer(raw.load(std::memory_order_relaxed)); return ExtractPointer(std::bit_cast<Data>(data_raw.load(std::memory_order_relaxed)), ignored_marked);
} }
/// Returns the page type attribute /// Returns the page type attribute
[[nodiscard]] PageType Type() const noexcept { [[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 /// Returns the page pointer and attribute pair, extracted from the same atomic read
[[nodiscard]] std::pair<uintptr_t, PageType> PointerType() const noexcept { [[nodiscard]] std::tuple<uintptr_t, PageType, u16> PointerTypeBlock(bool ignore_marked = false) const noexcept {
const uintptr_t non_atomic_raw = raw.load(std::memory_order_relaxed); const auto non_atomic_raw = std::bit_cast<Data>(data_raw.load(std::memory_order_relaxed));
return {ExtractPointer(non_atomic_raw), ExtractType(non_atomic_raw)}; 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. /// Write page info atomically
/// Use ExtractPointer and ExtractType to unpack the value. constexpr void Store(bool marked, PageType type, u16 block, uintptr_t pointer) noexcept {
[[nodiscard]] uintptr_t Raw() const noexcept { data_raw.store(std::bit_cast<u64>(Data{marked, type, block, pointer}));
return raw.load(std::memory_order_relaxed);
} }
/// Write a page pointer and type pair atomically constexpr void MarkRasterizerCached() noexcept {
void Store(uintptr_t pointer, PageType type) noexcept { data_raw.fetch_or(0b111);
raw.store(pointer | uintptr_t(type)); }
constexpr void MarkDebug(u64 ptr, u16 block) noexcept {
Store(true, PageType::DebugMemory, block, ptr);
} }
/// Unpack a pointer from a page info raw representation /// Unpack a pointer from a page info raw representation
[[nodiscard]] static uintptr_t ExtractPointer(uintptr_t raw) noexcept { [[nodiscard]] static uintptr_t ExtractPointer(Data raw, bool ignore_marked = false) noexcept {
return raw & (~uintptr_t{0} << ATTRIBUTE_BITS); 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 u16 ExtractBlock(Data raw) noexcept {
[[nodiscard]] static PageType ExtractType(uintptr_t raw) noexcept { return static_cast<u16>(raw.block | (raw.block2 << 9));
return static_cast<PageType>(raw & ((uintptr_t{1} << ATTRIBUTE_BITS) - 1));
} }
private: private:
std::atomic<uintptr_t> raw; std::atomic<u64> data_raw;
static_assert(sizeof(Data) == sizeof(std::atomic<u64>));
}; };
PageTable(); PageTable();
@@ -100,13 +129,8 @@ struct PageTable {
PageTable(const PageTable&) = delete; PageTable(const PageTable&) = delete;
PageTable& operator=(const PageTable&) = delete; PageTable& operator=(const PageTable&) = delete;
PageTable(PageTable&&) noexcept = delete;
PageTable(PageTable&&) noexcept = default; PageTable& operator=(PageTable&&) noexcept = delete;
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;
/** /**
* Resizes the page table to be able to accommodate enough pages within * 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; 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 /// Vector of memory pointers backing each page. An entry can only be non-null if the
/// corresponding attribute element is of type `Memory`. /// corresponding attribute element is of type `Memory`.
struct PageEntryData { SparseLargeVector<PageEntryData> entries;
PageInfo ptr; static_assert(sizeof(PageEntryData) == 8);
u64 block;
u64 addr;
u64 padding;
};
VirtualBuffer<PageEntryData> entries;
static_assert(sizeof(PageEntryData) == 32);
u8* fastmem_arena{}; u8* fastmem_arena{};
std::size_t current_address_space_width_in_bits{}; std::size_t current_address_space_width_in_bits{};
std::size_t page_size{}; std::size_t current_page_bits{};
}; };
} // namespace Common } // namespace Common
-79
View File
@@ -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
+7 -7
View File
@@ -389,7 +389,7 @@ struct Values {
true}; true};
SwitchableSetting<bool> frame_gen{linkage, false, "frame_gen", Category::Renderer, SwitchableSetting<bool> frame_gen{linkage, false, "frame_gen", Category::Renderer,
Specialization::Default, true, true}; Specialization::Default, true, false};
SwitchableSetting<u32, true> frame_gen_multiplier{linkage, SwitchableSetting<u32, true> frame_gen_multiplier{linkage,
2, 2,
@@ -399,7 +399,7 @@ struct Values {
Category::Renderer, Category::Renderer,
Specialization::Countable, Specialization::Countable,
true, true,
true, false,
&frame_gen}; &frame_gen};
SwitchableSetting<u32, true> frame_gen_target_rate{linkage, SwitchableSetting<u32, true> frame_gen_target_rate{linkage,
@@ -419,7 +419,7 @@ struct Values {
Category::Renderer, Category::Renderer,
Specialization::Default, Specialization::Default,
true, true,
true, false,
&frame_gen}; &frame_gen};
SwitchableSetting<u32, true> frame_gen_flow_scale{linkage, SwitchableSetting<u32, true> frame_gen_flow_scale{linkage,
@@ -442,11 +442,11 @@ struct Values {
Category::Renderer, Category::Renderer,
Specialization::Countable, Specialization::Countable,
true, true,
true, false,
&frame_gen}; &frame_gen};
SwitchableSetting<bool> frame_gen_fp16{linkage, true, "frame_gen_fp16", Category::Renderer, SwitchableSetting<bool> frame_gen_fp16{linkage, true, "frame_gen_fp16", Category::Renderer,
Specialization::Default, true, true, &frame_gen}; Specialization::Default, true, false, &frame_gen};
SwitchableSetting<bool> frame_gen_dump_flow{linkage, false, "frame_gen_dump_flow", SwitchableSetting<bool> frame_gen_dump_flow{linkage, false, "frame_gen_dump_flow",
Category::Renderer}; Category::Renderer};
@@ -858,7 +858,7 @@ struct Values {
SwitchableSetting<std::string> program_args{linkage, SwitchableSetting<std::string> program_args{linkage,
std::string(), std::string(),
"program_args", "program_args",
Category::System, Category::Debugging,
Specialization::Default, Specialization::Default,
true, // save_ - persist in config file true, // save_ - persist in config file
false}; // runtime_modifiable_ - startup-only false}; // runtime_modifiable_ - startup-only
@@ -904,7 +904,7 @@ struct Values {
0, 0,
65535, 65535,
"debug_knobs", "debug_knobs",
Category::System, Category::Debugging,
Specialization::Countable, Specialization::Countable,
true, true,
true}; true};
+143
View File
@@ -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
+194
View File
@@ -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
+88 -712
View File
@@ -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-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2014 Tony Wasserka // SPDX-FileCopyrightText: 2014 Tony Wasserka
@@ -7,752 +7,128 @@
#pragma once #pragma once
#ifdef __ARM_NEON
#include <arm_neon.h>
#endif
#include <cmath> #include <cmath>
#include <type_traits> #include <type_traits>
namespace Common { namespace Common {
template <typename T> template <typename T, size_t N>
class Vec2; class Vec {
template <typename T>
class Vec3;
template <typename T>
class Vec4;
template <typename T>
class Vec2 {
public: public:
T x{}; std::array<T, N> elems{};
T y{};
constexpr Vec2() = default; constexpr Vec() = default;
constexpr Vec2(const T& x_, const T& y_) : x(x_), y(y_) {} 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 Vec<decltype(T{} + T{}), N> operator+(const Vec o) const noexcept {
[[nodiscard]] constexpr Vec2<T2> Cast() const { Vec<decltype(T{} + T{}), N> r{};
return Vec2<T2>(static_cast<T2>(x), static_cast<T2>(y)); 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) { [[nodiscard]] constexpr Vec<decltype(T{} - T{}), N> operator-(const Vec o) const noexcept {
return Vec2{f, f}; Vec<decltype(T{} - T{}), N> r{};
} for (size_t i = 0; i < N; ++i)
r.elems[i] = elems[i] - o.elems[i];
[[nodiscard]] constexpr Vec2<decltype(T{} + T{})> operator+(const Vec2& other) const { return r;
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;
} }
constexpr Vec<T, N> operator-=(const Vec<T, N> o) noexcept { return *this = *this - o; }
template <typename U = T> template <typename U = T>
[[nodiscard]] constexpr Vec2<std::enable_if_t<std::is_signed_v<U>, U>> operator-() const { [[nodiscard]] constexpr Vec<std::enable_if_t<std::is_signed_v<U>, U>, N> operator-() const noexcept {
return {-x, -y}; Vec<U, N> r{};
} for (size_t i = 0; i < N; ++i)
[[nodiscard]] constexpr Vec2<decltype(T{} * T{})> operator*(const Vec2& other) const { r.elems[i] = -elems[i];
return {x * other.x, y * other.y}; 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> 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 TV = decltype(T{} * V{});
using C = std::common_type_t<T, V>; using C = std::common_type_t<T, V>;
Vec<TV, N> r{};
return { for (size_t i = 0; i < N; ++i)
static_cast<TV>(static_cast<C>(x) * static_cast<C>(f)), r.elems[i] = TV(C(elems[i]) * C(f));
static_cast<TV>(static_cast<C>(y) * static_cast<C>(f)), return r;
};
} }
template <typename V>
constexpr Vec<T, N> operator*=(const V f) noexcept { return *this = *this * f; }
template <typename V> template <typename V>
constexpr Vec2& operator*=(const V& f) { [[nodiscard]] constexpr Vec<decltype(T{} / V{}), N> operator/(const V f) const noexcept {
*this = *this * f;
return *this;
}
template <typename V>
[[nodiscard]] constexpr Vec2<decltype(T{} / V{})> operator/(const V& f) const {
using TV = decltype(T{} / V{}); using TV = decltype(T{} / V{});
using C = std::common_type_t<T, V>; using C = std::common_type_t<T, V>;
Vec<TV, N> r{};
return { for (size_t i = 0; i < N; ++i)
static_cast<TV>(static_cast<C>(x) / static_cast<C>(f)), r.elems[i] = TV(C(elems[i]) / C(f));
static_cast<TV>(static_cast<C>(y) / static_cast<C>(f)), return r;
};
} }
template <typename V> template <typename V>
constexpr Vec2& operator/=(const V& f) { constexpr Vec<T, N> operator/=(const V f) noexcept { return *this = *this / f; }
*this = *this / f;
return *this;
}
[[nodiscard]] constexpr T Length2() const { [[nodiscard]] constexpr T Length2() const noexcept {
return x * x + y * y; T r{};
for (size_t i = 0; i < N; ++i)
r += elems[i] * elems[i];
return r;
} }
// Only implemented for T=float // Only implemented for T=float
[[nodiscard]] float Length() const; [[nodiscard]] T Length() const { return T(std::sqrt(float(Length2()))); }
[[nodiscard]] float Normalize(); // returns the previous length, which is often useful [[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) { [[nodiscard]] std::array<decltype(-T{}), 16> ToMatrix() const {
return *((&x) + i); const T x2 = elems[0] * elems[0];
} const T y2 = elems[1] * elems[1];
[[nodiscard]] constexpr const T& operator[](std::size_t i) const { const T z2 = elems[2] * elems[2];
return *((&x) + i);
}
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);
}
};
template <typename T, typename V>
[[nodiscard]] constexpr Vec2<T> operator*(const V& f, const Vec2<T>& vec) {
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>;
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 { return {
static_cast<TV>(static_cast<C>(x) * static_cast<C>(f)), 1.0f - 2.0f * (y2 + z2),
static_cast<TV>(static_cast<C>(y) * static_cast<C>(f)), 2.0f * (xy + wz),
static_cast<TV>(static_cast<C>(z) * static_cast<C>(f)), 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 V> template <typename T, size_t N, typename V>
constexpr Vec3& operator*=(const V& f) { [[nodiscard]] constexpr Vec<T, N> operator*(const V f, const Vec<T, N> v) noexcept {
*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>; using C = std::common_type_t<T, V>;
Vec<T, N> r{};
return { for (size_t i = 0; i < N; ++i)
static_cast<TV>(static_cast<C>(x) / static_cast<C>(f)), r.elems[i] = T(C(f) * C(v.elems[i]));
static_cast<TV>(static_cast<C>(y) / static_cast<C>(f)), return r;
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]);
} }
} // namespace Common } // namespace Common
-44
View File
@@ -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
-84
View File
@@ -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
+1 -7
View File
@@ -711,8 +711,6 @@ add_library(core STATIC
hle/service/kernel_helpers.h hle/service/kernel_helpers.h
hle/service/lbl/lbl.cpp hle/service/lbl/lbl.cpp
hle/service/lbl/lbl.h 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.cpp
hle/service/ldn/lan_discovery.h hle/service/ldn/lan_discovery.h
hle/service/ldn/ldn.cpp 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_control.h
hle/service/ns/system_update_interface.cpp hle/service/ns/system_update_interface.cpp
hle/service/ns/system_update_interface.h 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.cpp
hle/service/nvdrv/core/container.h hle/service/nvdrv/core/container.h
hle/service/nvdrv/core/heap_mapper.cpp hle/service/nvdrv/core/heap_mapper.cpp
@@ -1067,8 +1063,6 @@ add_library(core STATIC
hle/service/sockets/sockets.h hle/service/sockets/sockets.h
hle/service/sockets/sockets_translate.cpp hle/service/sockets/sockets_translate.cpp
hle/service/sockets/sockets_translate.h 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.cpp
hle/service/spl/spl.h hle/service/spl/spl.h
hle/service/spl/spl_module.cpp hle/service/spl/spl_module.cpp
@@ -1247,7 +1241,7 @@ if (HAS_NCE)
target_link_libraries(core PRIVATE merry::oaknut) target_link_libraries(core PRIVATE merry::oaknut)
endif() 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 target_sources(core PRIVATE
arm/dynarmic/arm_dynarmic.h arm/dynarmic/arm_dynarmic.h
arm/dynarmic/arm_dynarmic_64.cpp arm/dynarmic/arm_dynarmic_64.cpp
+13 -5
View File
@@ -172,12 +172,12 @@ void ArmDynarmic32::MakeJit(Common::PageTable* page_table) {
if (page_table) { if (page_table) {
constexpr size_t PageBits = 12; constexpr size_t PageBits = 12;
constexpr size_t NumPageTableEntries = 1 << (32 - PageBits); 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()); // Dynarmic will not write to the page table, const_cast is safe here
config.page_table_pointer_mask_bits = Common::PageTable::ATTRIBUTE_BITS; config.page_table = reinterpret_cast<std::array<std::uint8_t*, NumPageTableEntries>*>(
config.page_table_log2_stride = PageLog2Stride; 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.absolute_offset_page_table = true;
config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128; config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128;
config.only_detect_misalignment_via_page_table_on_page_boundary = true; 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.fastmem_exclusive_access = config.fastmem_pointer != std::nullopt;
config.recompile_on_exclusive_fastmem_failure = true; 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 // Multi-process state
@@ -420,6 +427,7 @@ void ArmDynarmic32::SignalInterrupt(Kernel::KThread* thread) {
} }
void ArmDynarmic32::ClearInstructionCache() { void ArmDynarmic32::ClearInstructionCache() {
m_cb->last_code_addr = u64(-1);
m_jit->ClearCache(); m_jit->ClearCache();
} }
+13 -6
View File
@@ -211,13 +211,12 @@ void ArmDynarmic64::MakeJit(Common::PageTable* page_table, std::size_t address_s
// Memory // Memory
if (page_table) { if (page_table) {
constexpr size_t PageLog2Stride = 5; // Dynarmic will not write to the page table, const_cast is safe here
static_assert(1 << PageLog2Stride == sizeof(Common::PageTable::PageEntryData)); config.page_table = reinterpret_cast<void**>(
const_cast<Common::PageTable::PageEntryData*>(page_table->entries.data()));
config.page_table = reinterpret_cast<void**>(page_table->entries.data());
config.page_table_address_space_bits = std::uint32_t(address_space_bits); 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_pointer_mask = Common::PageTable::ATTRIBUTE_MASK;
config.page_table_log2_stride = PageLog2Stride; config.page_table_marked_bit = 0;
config.silently_mirror_page_table = false; config.silently_mirror_page_table = false;
config.absolute_offset_page_table = true; config.absolute_offset_page_table = true;
config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128; 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.fastmem_exclusive_access = config.fastmem_pointer != std::nullopt;
config.recompile_on_exclusive_fastmem_failure = true; 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 // Multi-process state
@@ -447,6 +453,7 @@ void ArmDynarmic64::SignalInterrupt(Kernel::KThread* thread) {
} }
void ArmDynarmic64::ClearInstructionCache() { void ArmDynarmic64::ClearInstructionCache() {
m_cb->last_code_addr = u64(-1);
m_jit->ClearCache(); m_jit->ClearCache();
} }
+4 -1
View File
@@ -121,7 +121,10 @@ union Exclusive {
constexpr explicit Exclusive(u32 raw_) : raw{raw_} {} constexpr explicit Exclusive(u32 raw_) : raw{raw_} {}
constexpr bool Verify() { 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() { constexpr u32 GetSig() {
+8 -2
View File
@@ -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-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -26,8 +29,11 @@ public:
template <typename T> template <typename T>
Common::PhysicalAddress GetPhysicalAddr(const T* ptr) const { Common::PhysicalAddress GetPhysicalAddr(const T* ptr) const {
return (reinterpret_cast<uintptr_t>(ptr) - return GetPhysicalAddr(reinterpret_cast<uintptr_t>(ptr));
reinterpret_cast<uintptr_t>(buffer.BackingBasePointer())) + }
Common::PhysicalAddress GetPhysicalAddr(uintptr_t ptr) const {
return (ptr - reinterpret_cast<uintptr_t>(buffer.BackingBasePointer())) +
DramMemoryMap::Base; DramMemoryMap::Base;
} }
+5 -5
View File
@@ -18,7 +18,7 @@
#include "common/common_types.h" #include "common/common_types.h"
#include "common/range_mutex.h" #include "common/range_mutex.h"
#include "common/scratch_buffer.h" #include "common/scratch_buffer.h"
#include "common/virtual_buffer.h" #include "common/sparse_large_vector.h"
namespace Core { namespace Core {
@@ -178,8 +178,8 @@ private:
u32 continuity_tracker; u32 continuity_tracker;
u32 compressed_physical_ptr; u32 compressed_physical_ptr;
}; };
Common::VirtualBuffer<u32> compressed_device_addr; Common::SparseLargeVector<u32> compressed_device_addr;
Common::VirtualBuffer<TrackedEntry> tracked_entries; Common::SparseLargeVector<TrackedEntry> tracked_entries;
// Process memory interfaces // Process memory interfaces
@@ -200,8 +200,8 @@ private:
return std::make_pair(asid, address); return std::make_pair(asid, address);
} }
void InsertCPUBacking(size_t page_index, VAddr address, Asid asid) { constexpr void InsertCPUBacking(size_t page_index, VAddr address, Asid asid) {
tracked_entries[page_index].cpu_backing_address = address | (asid.id << asid_start_bit); tracked_entries.GetUnchecked(page_index).cpu_backing_address = address | (asid.id << asid_start_bit);
} }
std::array<TranslationEntry, 4> t_slot{}; std::array<TranslationEntry, 4> t_slot{};
+22 -27
View File
@@ -177,17 +177,6 @@ DeviceMemoryManager<Traits>::DeviceMemoryManager(const DeviceMemory& device_memo
{ {
impl = std::make_unique<DeviceMemoryManagerAllocator<Traits>>(); impl = std::make_unique<DeviceMemoryManagerAllocator<Traits>>();
cached_pages = std::make_unique<CachedPages>(); 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> 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 start_page_d = address >> Memory::YUZU_PAGEBITS;
size_t num_pages = Common::AlignUp(size, Memory::YUZU_PAGESIZE) >> Memory::YUZU_PAGEBITS; size_t num_pages = Common::AlignUp(size, Memory::YUZU_PAGESIZE) >> Memory::YUZU_PAGEBITS;
std::scoped_lock lk(mapping_guard); 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++) { for (size_t i = 0; i < num_pages; i++) {
const VAddr new_vaddress = virtual_address + i * Memory::YUZU_PAGESIZE; const VAddr new_vaddress = virtual_address + i * Memory::YUZU_PAGESIZE;
auto* ptr = process_memory->GetPointerSilent(Common::ProcessAddress(new_vaddress)); auto* ptr = process_memory->GetPointerSilent(Common::ProcessAddress(new_vaddress));
if (ptr == nullptr) [[unlikely]] { 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; continue;
} }
auto phys_addr = static_cast<u32>(GetRawPhysicalAddr(ptr) >> Memory::YUZU_PAGEBITS) + 1U; 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); InsertCPUBacking(start_page_d + i, new_vaddress, asid);
const u32 base_dev = compressed_device_addr[phys_addr - 1U]; const u32 base_dev = compressed_device_addr[phys_addr - 1U];
const u32 new_dev = static_cast<u32>(start_page_d + i); const u32 new_dev = static_cast<u32>(start_page_d + i);
if (base_dev == 0) [[likely]] { if (base_dev == 0) [[likely]] {
compressed_device_addr[phys_addr - 1U] = new_dev; compressed_device_addr.GetAndFault(phys_addr - 1U) = new_dev;
continue; continue;
} }
u32 start_id = base_dev & MULTI_MASK; u32 start_id = base_dev & MULTI_MASK;
if ((base_dev >> MULTI_FLAG_BITS) == 0) { if ((base_dev >> MULTI_FLAG_BITS) == 0) {
start_id = impl->multi_dev_address.Register(base_dev); 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); 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; size_t num_pages = Common::AlignUp(size, Memory::YUZU_PAGESIZE) >> Memory::YUZU_PAGEBITS;
device_inter->InvalidateRegion(address, size); device_inter->InvalidateRegion(address, size);
std::scoped_lock lk(mapping_guard); 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++) { for (size_t i = 0; i < num_pages; i++) {
auto phys_addr = tracked_entries[start_page_d + i].compressed_physical_ptr; auto& entry = tracked_entries.GetUnchecked(start_page_d + i);
tracked_entries[start_page_d + i].compressed_physical_ptr = 0; auto phys_addr = entry.compressed_physical_ptr;
tracked_entries[start_page_d + i].cpu_backing_address = 0; entry.compressed_physical_ptr = 0;
entry.cpu_backing_address = 0;
if (phys_addr != 0) [[likely]] { 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]] { if ((base_dev >> MULTI_FLAG_BITS) == 0) [[likely]] {
compressed_device_addr[phys_addr - 1] = 0; base_dev = 0;
continue; continue;
} }
const auto [more_entries, new_start] = impl->multi_dev_address.Unregister( const auto [more_entries, new_start] = impl->multi_dev_address.Unregister(
static_cast<u32>(start_page_d + i), base_dev & MULTI_MASK); static_cast<u32>(start_page_d + i), base_dev & MULTI_MASK);
if (!more_entries) { if (!more_entries) {
compressed_device_addr[phys_addr - 1] = base_dev = impl->multi_dev_address.ReleaseEntry(new_start);
impl->multi_dev_address.ReleaseEntry(new_start);
continue; continue;
} }
compressed_device_addr[phys_addr - 1] = new_start | MULTI_FLAG; base_dev = new_start | MULTI_FLAG;
} }
} }
t_slot = {}; 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; size_t num_pages = Common::AlignUp(size, Memory::YUZU_PAGESIZE) >> Memory::YUZU_PAGEBITS;
uintptr_t last_ptr = 0; uintptr_t last_ptr = 0;
size_t page_count = 1; 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--) { for (size_t i = num_pages; i > 0; i--) {
size_t index = i - 1; size_t index = i - 1;
const VAddr new_vaddress = virtual_address + index * Memory::YUZU_PAGESIZE; 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; page_count = 1;
} }
last_ptr = new_ptr; 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> template <typename Traits>
u8* DeviceMemoryManager<Traits>::GetSpan(const DAddr src_addr, const std::size_t size) { u8* DeviceMemoryManager<Traits>::GetSpan(const DAddr src_addr, const std::size_t size) {
size_t page_index = src_addr >> page_bits; size_t page_index = src_addr >> page_bits;
size_t subbits = src_addr & page_mask; 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 GetPointer<u8>(src_addr);
} }
return nullptr; return nullptr;
@@ -313,7 +308,7 @@ template <typename Traits>
const u8* DeviceMemoryManager<Traits>::GetSpan(const DAddr src_addr, const std::size_t size) const { 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 page_index = src_addr >> page_bits;
size_t subbits = src_addr & page_mask; 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 GetPointer<u8>(src_addr);
} }
return nullptr; 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_index = addr >> Memory::YUZU_PAGEBITS;
std::size_t page_offset = addr & Memory::YUZU_PAGEMASK; std::size_t page_offset = addr & Memory::YUZU_PAGEMASK;
while (remaining_size) { 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 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); const auto current_vaddr = u64((page_index << Memory::YUZU_PAGEBITS) + page_offset);
SCOPE_EXIT{ 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-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // 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 { size_t HierarchicalIntegrityVerificationStorage::GetSize() const {
ASSERT(m_data_size >= 0);
return m_data_size; return m_data_size;
} }
+12 -5
View File
@@ -120,7 +120,7 @@ std::array<u8, 32> IPSwitchCompiler::GetBuildID() const {
static IPSwitchRecord EscapeStringSequences(std::string_view sv) { static IPSwitchRecord EscapeStringSequences(std::string_view sv) {
IPSwitchRecord r{}; IPSwitchRecord r{};
for (auto it = sv.cbegin(); it != sv.cend(); ) { for (auto it = sv.cbegin(); it < sv.cend(); ) {
if (*it == '\\' && it + 1 < sv.cend()) { if (*it == '\\' && it + 1 < sv.cend()) {
switch (it[1]) { switch (it[1]) {
case 'a': r.data[r.count] = '\a'; break; 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); LOG_WARNING(Loader, "Unknown flag {}", line);
break; break;
} }
} else if (patches.empty()) {
LOG_WARNING(Loader, "Invalid line not in a patch {}", line);
} else { } else {
size_t offset = size_t(std::strtoul(line.data(), nullptr, 16)); size_t offset = size_t(std::strtoul(line.data(), nullptr, 16));
offset += size_t(offset_shift); offset += size_t(offset_shift);
@@ -253,28 +255,33 @@ void IPSwitchCompiler::Parse(std::span<u8 const> bytes) {
// now make a nominal preprocessed line: remove comments // now make a nominal preprocessed line: remove comments
char quote = '\0'; char quote = '\0';
auto const sline_start = p; auto const sline_start = p;
auto last_char = p;
for (; p < sline.cend(); ) { for (; p < sline.cend(); ) {
// we dont check for "//", IPS checks for '/' only... // 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] == '#')) { || (!quote && p[0] == '#')) {
break; break;
} else if (p[0] == '\"' || p[0] == '\'') { } else if (p[0] == '\"' || p[0] == '\'') {
quote = (p[0] == quote) ? '\0' : p[0]; quote = (p[0] == quote) ? '\0' : p[0];
++p; ++p;
last_char = p;
} else if (p + 1 < sline.cend() && p[0] == '\\') { } else if (p + 1 < sline.cend() && p[0] == '\\') {
p += 2; p += 2;
last_char = p;
} else { } else {
++p; ++p;
last_char = p;
} }
} }
// now we have the preprocessed string ;) // now we have the preprocessed string ;)
std::string_view pp_str(sline_start, p); std::string_view const pp_str(sline_start, last_char);
if (pp_str.size() > 0 && !parse_line(pp_str)) { if (pp_str.size() > 0 && !parse_line(pp_str))
break; break;
} }
} }
} }
}
VirtualFile IPSwitchCompiler::Apply(const VirtualFile& in) const { VirtualFile IPSwitchCompiler::Apply(const VirtualFile& in) const {
if (in == nullptr) if (in == nullptr)
+70 -40
View File
@@ -635,6 +635,36 @@ Result KPageTableBase::CheckMemoryState(const KMemoryInfo& info, KMemoryState st
R_SUCCEED(); 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, Result KPageTableBase::CheckMemoryStateContiguous(size_t* out_blocks_needed, KProcessAddress addr,
size_t size, KMemoryState state_mask, size_t size, KMemoryState state_mask,
KMemoryState state, KMemoryPermission perm_mask, KMemoryState state, KMemoryPermission perm_mask,
@@ -940,7 +970,7 @@ Result KPageTableBase::QueryMappingImpl(KProcessAddress* out, KPhysicalAddress a
size_t tot_size = 0; size_t tot_size = 0;
next_valid = 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 =
(next_entry.block_size - (GetInteger(region_start) & (next_entry.block_size - 1))); (next_entry.block_size - (GetInteger(region_start) & (next_entry.block_size - 1)));
@@ -976,7 +1006,7 @@ Result KPageTableBase::QueryMappingImpl(KProcessAddress* out, KPhysicalAddress a
break; 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. // Check the last entry.
@@ -1754,7 +1784,7 @@ Result KPageTableBase::MakePageGroup(KPageGroup& pg, KProcessAddress addr, size_
// Begin traversal. // Begin traversal.
TraversalContext context; TraversalContext context;
TraversalEntry next_entry; 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); ResultInvalidCurrentMemory);
// Prepare tracking variables. // Prepare tracking variables.
@@ -1764,7 +1794,7 @@ Result KPageTableBase::MakePageGroup(KPageGroup& pg, KProcessAddress addr, size_
// Iterate, adding to group as we go. // Iterate, adding to group as we go.
while (tot_size < size) { 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); ResultInvalidCurrentMemory);
if (next_entry.phys_addr != (cur_addr + cur_size)) { if (next_entry.phys_addr != (cur_addr + cur_size)) {
@@ -1828,7 +1858,7 @@ bool KPageTableBase::IsValidPageGroup(const KPageGroup& pg, KProcessAddress addr
// Begin traversal. // Begin traversal.
TraversalContext context; TraversalContext context;
TraversalEntry next_entry; 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; return false;
} }
@@ -1839,7 +1869,7 @@ bool KPageTableBase::IsValidPageGroup(const KPageGroup& pg, KProcessAddress addr
// Iterate, comparing expected to actual. // Iterate, comparing expected to actual.
while (tot_size < size) { 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; return false;
} }
@@ -1896,7 +1926,7 @@ Result KPageTableBase::GetContiguousMemoryRangeWithState(
// Begin a traversal. // Begin a traversal.
TraversalContext context; TraversalContext context;
TraversalEntry cur_entry = {.phys_addr = 0, .block_size = 0}; 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); ResultInvalidCurrentMemory);
// Traverse until we have enough size or we aren't contiguous any more. // Traverse until we have enough size or we aren't contiguous any more.
@@ -1905,7 +1935,7 @@ Result KPageTableBase::GetContiguousMemoryRangeWithState(
for (contig_size = for (contig_size =
cur_entry.block_size - (GetInteger(phys_address) & (cur_entry.block_size - 1)); cur_entry.block_size - (GetInteger(phys_address) & (cur_entry.block_size - 1));
contig_size < size; contig_size += cur_entry.block_size) { 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; break;
} }
if (cur_entry.phys_addr != phys_address + contig_size) { if (cur_entry.phys_addr != phys_address + contig_size) {
@@ -2334,7 +2364,7 @@ Result KPageTableBase::QueryPhysicalAddress(Svc::lp64::PhysicalMemoryInfo* out,
TraversalContext context; TraversalContext context;
TraversalEntry next_entry; TraversalEntry next_entry;
bool traverse_valid = 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); R_UNLESS(traverse_valid, ResultInvalidCurrentMemory);
// Set tracking variables. // Set tracking variables.
@@ -2345,7 +2375,7 @@ Result KPageTableBase::QueryPhysicalAddress(Svc::lp64::PhysicalMemoryInfo* out,
while (true) { while (true) {
// Continue the traversal. // Continue the traversal.
traverse_valid = 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) { if (!traverse_valid) {
break; break;
} }
@@ -2567,7 +2597,7 @@ Result KPageTableBase::UnmapIoRegion(KProcessAddress dst_address, KPhysicalAddre
TraversalContext context; TraversalContext context;
TraversalEntry next_entry; TraversalEntry next_entry;
ASSERT( 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. // Check that the physical region matches.
R_UNLESS(next_entry.phys_addr == phys_addr, ResultInvalidMemoryRegion); 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)); next_entry.block_size - (GetInteger(phys_addr) & (next_entry.block_size - 1));
checked_size < size; checked_size += next_entry.block_size) { checked_size < size; checked_size += next_entry.block_size) {
// Continue the traversal. // 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. // Check that the physical region matches.
R_UNLESS(next_entry.phys_addr == phys_addr + checked_size, ResultInvalidMemoryRegion); R_UNLESS(next_entry.phys_addr == phys_addr + checked_size, ResultInvalidMemoryRegion);
@@ -3029,7 +3059,7 @@ Result KPageTableBase::InvalidateProcessDataCache(KProcessAddress address, size_
TraversalContext context; TraversalContext context;
TraversalEntry next_entry; TraversalEntry next_entry;
bool traverse_valid = 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); R_UNLESS(traverse_valid, ResultInvalidCurrentMemory);
// Prepare tracking variables. // Prepare tracking variables.
@@ -3041,7 +3071,7 @@ Result KPageTableBase::InvalidateProcessDataCache(KProcessAddress address, size_
while (tot_size < size) { while (tot_size < size) {
// Continue the traversal. // Continue the traversal.
traverse_valid = 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); R_UNLESS(traverse_valid, ResultInvalidCurrentMemory);
if (next_entry.phys_addr != (cur_addr + cur_size)) { if (next_entry.phys_addr != (cur_addr + cur_size)) {
@@ -3129,7 +3159,7 @@ Result KPageTableBase::ReadDebugMemory(KProcessAddress dst_address, KProcessAddr
TraversalContext context; TraversalContext context;
TraversalEntry next_entry; TraversalEntry next_entry;
bool traverse_valid = 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); R_UNLESS(traverse_valid, ResultInvalidCurrentMemory);
// Prepare tracking variables. // Prepare tracking variables.
@@ -3167,7 +3197,7 @@ Result KPageTableBase::ReadDebugMemory(KProcessAddress dst_address, KProcessAddr
while (tot_size < size) { while (tot_size < size) {
// Continue the traversal. // Continue the traversal.
traverse_valid = traverse_valid =
impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context)); ContinueTraversal(impl, std::addressof(next_entry), std::addressof(context));
ASSERT(traverse_valid); ASSERT(traverse_valid);
if (next_entry.phys_addr != (cur_addr + cur_size)) { if (next_entry.phys_addr != (cur_addr + cur_size)) {
@@ -3225,7 +3255,7 @@ Result KPageTableBase::WriteDebugMemory(KProcessAddress dst_address, KProcessAdd
TraversalContext context; TraversalContext context;
TraversalEntry next_entry; TraversalEntry next_entry;
bool traverse_valid = 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); R_UNLESS(traverse_valid, ResultInvalidCurrentMemory);
// Prepare tracking variables. // Prepare tracking variables.
@@ -3267,7 +3297,7 @@ Result KPageTableBase::WriteDebugMemory(KProcessAddress dst_address, KProcessAdd
while (tot_size < size) { while (tot_size < size) {
// Continue the traversal. // Continue the traversal.
traverse_valid = traverse_valid =
impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context)); ContinueTraversal(impl, std::addressof(next_entry), std::addressof(context));
ASSERT(traverse_valid); ASSERT(traverse_valid);
if (next_entry.phys_addr != (cur_addr + cur_size)) { if (next_entry.phys_addr != (cur_addr + cur_size)) {
@@ -3728,7 +3758,7 @@ Result KPageTableBase::CopyMemoryFromLinearToUser(
TraversalContext context; TraversalContext context;
TraversalEntry next_entry; TraversalEntry next_entry;
bool traverse_valid = 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); ASSERT(traverse_valid);
// Prepare tracking variables. // Prepare tracking variables.
@@ -3768,7 +3798,7 @@ Result KPageTableBase::CopyMemoryFromLinearToUser(
while (tot_size < size) { while (tot_size < size) {
// Continue the traversal. // Continue the traversal.
traverse_valid = traverse_valid =
impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context)); ContinueTraversal(impl, std::addressof(next_entry), std::addressof(context));
ASSERT(traverse_valid); ASSERT(traverse_valid);
if (next_entry.phys_addr != (cur_addr + cur_size)) { if (next_entry.phys_addr != (cur_addr + cur_size)) {
@@ -3822,7 +3852,7 @@ Result KPageTableBase::CopyMemoryFromLinearToKernel(
TraversalContext context; TraversalContext context;
TraversalEntry next_entry; TraversalEntry next_entry;
bool traverse_valid = 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); ASSERT(traverse_valid);
// Prepare tracking variables. // Prepare tracking variables.
@@ -3845,7 +3875,7 @@ Result KPageTableBase::CopyMemoryFromLinearToKernel(
while (tot_size < size) { while (tot_size < size) {
// Continue the traversal. // Continue the traversal.
traverse_valid = traverse_valid =
impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context)); ContinueTraversal(impl, std::addressof(next_entry), std::addressof(context));
ASSERT(traverse_valid); ASSERT(traverse_valid);
if (next_entry.phys_addr != (cur_addr + cur_size)) { if (next_entry.phys_addr != (cur_addr + cur_size)) {
@@ -3902,7 +3932,7 @@ Result KPageTableBase::CopyMemoryFromUserToLinear(
TraversalContext context; TraversalContext context;
TraversalEntry next_entry; TraversalEntry next_entry;
bool traverse_valid = 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); ASSERT(traverse_valid);
// Prepare tracking variables. // Prepare tracking variables.
@@ -3941,7 +3971,7 @@ Result KPageTableBase::CopyMemoryFromUserToLinear(
while (tot_size < size) { while (tot_size < size) {
// Continue the traversal. // Continue the traversal.
traverse_valid = traverse_valid =
impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context)); ContinueTraversal(impl, std::addressof(next_entry), std::addressof(context));
ASSERT(traverse_valid); ASSERT(traverse_valid);
if (next_entry.phys_addr != (cur_addr + cur_size)) { if (next_entry.phys_addr != (cur_addr + cur_size)) {
@@ -3997,7 +4027,7 @@ Result KPageTableBase::CopyMemoryFromKernelToLinear(KProcessAddress dst_addr, si
TraversalContext context; TraversalContext context;
TraversalEntry next_entry; TraversalEntry next_entry;
bool traverse_valid = 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); ASSERT(traverse_valid);
// Prepare tracking variables. // Prepare tracking variables.
@@ -4020,7 +4050,7 @@ Result KPageTableBase::CopyMemoryFromKernelToLinear(KProcessAddress dst_addr, si
while (tot_size < size) { while (tot_size < size) {
// Continue the traversal. // Continue the traversal.
traverse_valid = traverse_valid =
impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context)); ContinueTraversal(impl, std::addressof(next_entry), std::addressof(context));
ASSERT(traverse_valid); ASSERT(traverse_valid);
if (next_entry.phys_addr != (cur_addr + cur_size)) { if (next_entry.phys_addr != (cur_addr + cur_size)) {
@@ -4089,10 +4119,10 @@ Result KPageTableBase::CopyMemoryFromHeapToHeap(
bool traverse_valid; bool traverse_valid;
// Begin traversal. // 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); std::addressof(src_context), src_addr);
ASSERT(traverse_valid); 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); std::addressof(dst_context), dst_addr);
ASSERT(traverse_valid); ASSERT(traverse_valid);
@@ -4127,7 +4157,7 @@ Result KPageTableBase::CopyMemoryFromHeapToHeap(
if (ofs + cur_copy_size != size) { if (ofs + cur_copy_size != size) {
if (cur_src_addr + cur_min_size == cur_src_block_addr + cur_src_size) { if (cur_src_addr + cur_min_size == cur_src_block_addr + cur_src_size) {
// Continue the src traversal. // 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)); std::addressof(src_context));
ASSERT(traverse_valid); ASSERT(traverse_valid);
@@ -4138,7 +4168,7 @@ Result KPageTableBase::CopyMemoryFromHeapToHeap(
if (cur_dst_addr + cur_min_size == if (cur_dst_addr + cur_min_size ==
dst_next_entry.phys_addr + dst_next_entry.block_size) { dst_next_entry.phys_addr + dst_next_entry.block_size) {
// Continue the dst traversal. // 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)); std::addressof(dst_context));
ASSERT(traverse_valid); ASSERT(traverse_valid);
@@ -4223,10 +4253,10 @@ Result KPageTableBase::CopyMemoryFromHeapToHeapWithoutCheckDestination(
bool traverse_valid; bool traverse_valid;
// Begin traversal. // 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); std::addressof(src_context), src_addr);
ASSERT(traverse_valid); 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); std::addressof(dst_context), dst_addr);
ASSERT(traverse_valid); ASSERT(traverse_valid);
@@ -4261,7 +4291,7 @@ Result KPageTableBase::CopyMemoryFromHeapToHeapWithoutCheckDestination(
if (ofs + cur_copy_size != size) { if (ofs + cur_copy_size != size) {
if (cur_src_addr + cur_min_size == cur_src_block_addr + cur_src_size) { if (cur_src_addr + cur_min_size == cur_src_block_addr + cur_src_size) {
// Continue the src traversal. // 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)); std::addressof(src_context));
ASSERT(traverse_valid); ASSERT(traverse_valid);
@@ -4272,7 +4302,7 @@ Result KPageTableBase::CopyMemoryFromHeapToHeapWithoutCheckDestination(
if (cur_dst_addr + cur_min_size == if (cur_dst_addr + cur_min_size ==
dst_next_entry.phys_addr + dst_next_entry.block_size) { dst_next_entry.phys_addr + dst_next_entry.block_size) {
// Continue the dst traversal. // 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)); std::addressof(dst_context));
ASSERT(traverse_valid); ASSERT(traverse_valid);
@@ -4547,7 +4577,7 @@ Result KPageTableBase::SetupForIpcServer(KProcessAddress* out_addr, size_t size,
// Begin traversal. // Begin traversal.
TraversalContext context; TraversalContext context;
TraversalEntry next_entry; 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); std::addressof(context), aligned_src_start);
ASSERT(traverse_valid); 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 the block's size was one page, we may need to continue traversal.
if (cur_block_size == 0 && aligned_src_size > PageSize) { if (cur_block_size == 0 && aligned_src_size > PageSize) {
traverse_valid = 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); ASSERT(traverse_valid);
cur_block_addr = next_entry.phys_addr; 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) { while (aligned_src_start + tot_block_size < mapping_src_end) {
// Continue the traversal. // Continue the traversal.
traverse_valid = 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); ASSERT(traverse_valid);
// Process the block. // 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 && if (mapped_block_end + cur_block_size < aligned_src_end &&
cur_block_size == last_block_size) { cur_block_size == last_block_size) {
traverse_valid = 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); ASSERT(traverse_valid);
cur_block_addr = next_entry.phys_addr; 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) ContiguousRangeInfo(KPageTableBase& pt, KProcessAddress address, size_t size)
: m_pt(pt), m_remaining_size(size) { : m_pt(pt), m_remaining_size(size) {
// Begin a traversal. // 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)); std::addressof(m_context), address));
// Setup tracking fields. // Setup tracking fields.
@@ -5632,7 +5662,7 @@ Result KPageTableBase::UnmapProcessMemory(KProcessAddress dst_address, size_t si
void DetermineContiguousBlockExtents() { void DetermineContiguousBlockExtents() {
// Continue traversing until we're not contiguous, or we have enough. // Continue traversing until we're not contiguous, or we have enough.
while (m_cur_size < m_remaining_size) { 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))); std::addressof(m_context)));
// If we're not contiguous, we're done. // If we're not contiguous, we're done.
+12 -1
View File
@@ -370,6 +370,10 @@ private:
size_t num_pages, size_t alignment, size_t offset, size_t num_pages, size_t alignment, size_t offset,
size_t guard_pages) const; 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, Result CheckMemoryStateContiguous(size_t* out_blocks_needed, KProcessAddress addr, size_t size,
KMemoryState state_mask, KMemoryState state, KMemoryState state_mask, KMemoryState state,
KMemoryPermission perm_mask, KMemoryPermission perm, KMemoryPermission perm_mask, KMemoryPermission perm,
@@ -474,7 +478,14 @@ private:
// Validate pre-conditions. // Validate pre-conditions.
ASSERT(this->IsLockedByCurrentThread()); 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: public:
+7 -6
View File
@@ -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-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-late // SPDX-License-Identifier: GPL-2.0-or-late
@@ -2501,14 +2501,15 @@ void Call(Core::System& system, u32 imm) {
auto& process = GetCurrentProcess(kernel); auto& process = GetCurrentProcess(kernel);
std::array<uint64_t, 8> args; std::array<uint64_t, 8> args;
kernel.CurrentPhysicalCore().SaveSvcArguments(process, 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(); //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); Call64(system, imm, args);
else } else {
Call32(system, imm, args); Call32(system, imm, args);
}
//kernel.ExitSVCProfile(); //kernel.ExitSVCProfile();
kernel.CurrentPhysicalCore().LoadSvcArguments(process, args); kernel.CurrentPhysicalCore().LoadSvcArguments(process, args);
} }
+55 -69
View File
@@ -10,17 +10,15 @@
#include "core/hle/kernel/k_process.h" #include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_resource_limit.h" #include "core/hle/kernel/k_resource_limit.h"
#include "core/hle/kernel/svc.h" #include "core/hle/kernel/svc.h"
#include "core/hle/kernel/svc_results.h"
#include "core/hle/kernel/svc_version.h"
namespace Kernel::Svc { namespace Kernel::Svc {
/// Gets system/memory information for the current process /// Gets system/memory information for the current process
Result GetInfo(Core::System& system, u64* result, InfoType info_id_type, Handle handle, Result GetInfo(Core::System& system, u64* result, InfoType info_id_type, Handle handle, u64 info_sub_id) {
u64 info_sub_id) { LOG_TRACE(Kernel_SVC, "called info_id={:#x}, info_sub_id={:#x}, handle={:#08x}", info_id_type, info_sub_id, handle);
LOG_TRACE(Kernel_SVC, "called info_id={:#x}, info_sub_id={:#x}, handle={:#08x}", u32 info_id = u32(info_id_type);
info_id_type, info_sub_id, handle);
u32 info_id = static_cast<u32>(info_id_type);
switch (info_id_type) { switch (info_id_type) {
case InfoType::CoreMask: case InfoType::CoreMask:
case InfoType::PriorityMask: case InfoType::PriorityMask:
@@ -53,152 +51,123 @@ Result GetInfo(Core::System& system, u64* result, InfoType info_id_type, Handle
case InfoType::CoreMask: case InfoType::CoreMask:
*result = process->GetCoreMask(); *result = process->GetCoreMask();
R_SUCCEED(); R_SUCCEED();
case InfoType::PriorityMask: case InfoType::PriorityMask:
*result = process->GetPriorityMask(); *result = process->GetPriorityMask();
R_SUCCEED(); R_SUCCEED();
case InfoType::AliasRegionAddress: case InfoType::AliasRegionAddress:
*result = GetInteger(process->GetPageTable().GetAliasRegionStart()); *result = GetInteger(process->GetPageTable().GetAliasRegionStart());
R_SUCCEED(); R_SUCCEED();
case InfoType::AliasRegionSize: case InfoType::AliasRegionSize:
*result = process->GetPageTable().GetAliasRegionSize(); *result = process->GetPageTable().GetAliasRegionSize();
R_SUCCEED(); R_SUCCEED();
case InfoType::HeapRegionAddress: case InfoType::HeapRegionAddress:
*result = GetInteger(process->GetPageTable().GetHeapRegionStart()); *result = GetInteger(process->GetPageTable().GetHeapRegionStart());
R_SUCCEED(); R_SUCCEED();
case InfoType::HeapRegionSize: case InfoType::HeapRegionSize:
*result = process->GetPageTable().GetHeapRegionSize(); *result = process->GetPageTable().GetHeapRegionSize();
R_SUCCEED(); R_SUCCEED();
case InfoType::AslrRegionAddress: case InfoType::AslrRegionAddress:
*result = GetInteger(process->GetPageTable().GetAliasCodeRegionStart()); *result = GetInteger(process->GetPageTable().GetAliasCodeRegionStart());
R_SUCCEED(); R_SUCCEED();
case InfoType::AslrRegionSize: case InfoType::AslrRegionSize:
*result = process->GetPageTable().GetAliasCodeRegionSize(); *result = process->GetPageTable().GetAliasCodeRegionSize();
R_SUCCEED(); R_SUCCEED();
case InfoType::StackRegionAddress: case InfoType::StackRegionAddress:
*result = GetInteger(process->GetPageTable().GetStackRegionStart()); *result = GetInteger(process->GetPageTable().GetStackRegionStart());
R_SUCCEED(); R_SUCCEED();
case InfoType::StackRegionSize: case InfoType::StackRegionSize:
*result = process->GetPageTable().GetStackRegionSize(); *result = process->GetPageTable().GetStackRegionSize();
R_SUCCEED(); R_SUCCEED();
case InfoType::TotalMemorySize: case InfoType::TotalMemorySize:
*result = process->GetTotalUserPhysicalMemorySize(system.Kernel()); *result = process->GetTotalUserPhysicalMemorySize(system.Kernel());
R_SUCCEED(); R_SUCCEED();
case InfoType::UsedMemorySize: case InfoType::UsedMemorySize:
*result = process->GetUsedUserPhysicalMemorySize(system.Kernel()); *result = process->GetUsedUserPhysicalMemorySize(system.Kernel());
R_SUCCEED(); R_SUCCEED();
case InfoType::SystemResourceSizeTotal: case InfoType::SystemResourceSizeTotal:
*result = process->GetTotalSystemResourceSize(); *result = process->GetTotalSystemResourceSize();
R_SUCCEED(); R_SUCCEED();
case InfoType::SystemResourceSizeUsed: case InfoType::SystemResourceSizeUsed:
*result = process->GetUsedSystemResourceSize(); *result = process->GetUsedSystemResourceSize();
R_SUCCEED(); R_SUCCEED();
case InfoType::ProgramId: case InfoType::ProgramId:
*result = process->GetProgramId(); *result = process->GetProgramId();
R_SUCCEED(); R_SUCCEED();
case InfoType::UserExceptionContextAddress: case InfoType::UserExceptionContextAddress:
*result = GetInteger(process->GetProcessLocalRegionAddress()); *result = GetInteger(process->GetProcessLocalRegionAddress());
R_SUCCEED(); R_SUCCEED();
case InfoType::TotalNonSystemMemorySize: case InfoType::TotalNonSystemMemorySize:
*result = process->GetTotalNonSystemUserPhysicalMemorySize(system.Kernel()); *result = process->GetTotalNonSystemUserPhysicalMemorySize(system.Kernel());
R_SUCCEED(); R_SUCCEED();
case InfoType::UsedNonSystemMemorySize: case InfoType::UsedNonSystemMemorySize:
*result = process->GetUsedNonSystemUserPhysicalMemorySize(system.Kernel()); *result = process->GetUsedNonSystemUserPhysicalMemorySize(system.Kernel());
R_SUCCEED(); R_SUCCEED();
case InfoType::IsApplication: case InfoType::IsApplication:
*result = process->IsApplication(); *result = process->IsApplication();
R_SUCCEED(); R_SUCCEED();
case InfoType::FreeThreadCount: case InfoType::FreeThreadCount:
if (KResourceLimit* resource_limit = process->GetResourceLimit(); if (KResourceLimit* resource_limit = process->GetResourceLimit();
resource_limit != nullptr) { resource_limit != nullptr) {
const auto current_value = const auto current_value = resource_limit->GetCurrentValue(Svc::LimitableResource::ThreadCountMax);
resource_limit->GetCurrentValue(Svc::LimitableResource::ThreadCountMax); const auto limit_value = resource_limit->GetLimitValue(Svc::LimitableResource::ThreadCountMax);
const auto limit_value =
resource_limit->GetLimitValue(Svc::LimitableResource::ThreadCountMax);
*result = limit_value - current_value; *result = limit_value - current_value;
} else { } else {
*result = 0; *result = 0;
} }
R_SUCCEED(); R_SUCCEED();
case InfoType::AliasRegionExtraSize: { case InfoType::AliasRegionExtraSize: {
if (info_sub_id != 0) { R_UNLESS(info_sub_id == 0, ResultInvalidCombination);
return ResultInvalidCombination;
}
KProcess* current_process = GetCurrentProcessPointer(system.Kernel()); KProcess* current_process = GetCurrentProcessPointer(system.Kernel());
*result = current_process->GetPageTable().GetAliasRegionExtraSize(); *result = current_process->GetPageTable().GetAliasRegionExtraSize();
R_SUCCEED(); R_SUCCEED();
} }
default: default:
break; break;
} }
LOG_ERROR(Kernel_SVC, "Unimplemented svcGetInfo id={:#016x}", info_id); LOG_ERROR(Kernel_SVC, "Unimplemented svcGetInfo id={:#016x}", info_id);
R_THROW(ResultInvalidEnumValue); R_THROW(ResultInvalidEnumValue);
} }
case InfoType::DebuggerAttached: case InfoType::DebuggerAttached:
*result = 0; *result = 0;
R_SUCCEED(); R_SUCCEED();
case InfoType::ResourceLimit: { case InfoType::ResourceLimit: {
R_UNLESS(handle == 0, ResultInvalidHandle); R_UNLESS(handle == 0, ResultInvalidHandle);
R_UNLESS(info_sub_id == 0, ResultInvalidCombination); R_UNLESS(info_sub_id == 0, ResultInvalidCombination);
KProcess* const current_process = GetCurrentProcessPointer(system.Kernel()); KProcess* const current_process = GetCurrentProcessPointer(system.Kernel());
KHandleTable& handle_table = current_process->GetHandleTable(); KHandleTable& handle_table = current_process->GetHandleTable();
const auto resource_limit = current_process->GetResourceLimit(); if (auto const resource_limit = current_process->GetResourceLimit(); resource_limit) {
if (!resource_limit) {
*result = Svc::InvalidHandle;
// Yes, the kernel considers this a successful operation.
R_SUCCEED();
}
Handle resource_handle{}; Handle resource_handle{};
R_TRY(handle_table.Add(system.Kernel(), std::addressof(resource_handle), resource_limit)); R_TRY(handle_table.Add(system.Kernel(), std::addressof(resource_handle), resource_limit));
*result = resource_handle; *result = resource_handle;
} else {
*result = Svc::InvalidHandle;
}
// Yes, the kernel considers this a successful operation either way.
R_SUCCEED(); R_SUCCEED();
} }
case InfoType::RandomEntropy: case InfoType::RandomEntropy:
R_UNLESS(handle == 0, ResultInvalidHandle); R_UNLESS(handle == 0, ResultInvalidHandle);
R_UNLESS(info_sub_id < 4, ResultInvalidCombination); R_UNLESS(info_sub_id < 4, ResultInvalidCombination);
*result = GetCurrentProcess(system.Kernel()).GetRandomEntropy(info_sub_id); *result = GetCurrentProcess(system.Kernel()).GetRandomEntropy(info_sub_id);
R_SUCCEED(); R_SUCCEED();
case InfoType::InitialProcessIdRange: {
case InfoType::InitialProcessIdRange: LOG_WARNING(Kernel_SVC, "(STUBBED) Attempted to query privileged process id bounds, returned 0/64");
LOG_WARNING(Kernel_SVC, R_UNLESS(handle == InvalidHandle, ResultInvalidHandle);
"(STUBBED) Attempted to query privileged process id bounds, returned 0"); switch (InitialProcessIdRangeInfo(info_sub_id)) {
*result = 0; case InitialProcessIdRangeInfo::Minimum:
*result = 0; //todo
R_SUCCEED(); R_SUCCEED();
case InitialProcessIdRangeInfo::Maximum:
*result = 64; //todo
R_SUCCEED();
default:
R_THROW(ResultInvalidCombination);
}
}
case InfoType::ThreadTickCount: { case InfoType::ThreadTickCount: {
constexpr u64 num_cpus = 4; constexpr u64 num_cpus = 4;
if (info_sub_id != 0xFFFFFFFFFFFFFFFF && info_sub_id >= num_cpus) { 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, LOG_ERROR(Kernel_SVC, "Core count is out of range, expected {} but got {}", num_cpus, info_sub_id);
info_sub_id);
R_THROW(ResultInvalidCombination); R_THROW(ResultInvalidCombination);
} }
@@ -206,8 +175,7 @@ Result GetInfo(Core::System& system, u64* result, InfoType info_id_type, Handle
.GetHandleTable() .GetHandleTable()
.GetObject<KThread>(system.Kernel(), Handle(handle)); .GetObject<KThread>(system.Kernel(), Handle(handle));
if (thread.IsNull()) { if (thread.IsNull()) {
LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle={:#08x}", LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle={:#08x}", Handle(handle));
static_cast<Handle>(handle));
R_THROW(ResultInvalidHandle); R_THROW(ResultInvalidHandle);
} }
@@ -220,7 +188,6 @@ Result GetInfo(Core::System& system, u64* result, InfoType info_id_type, Handle
u64 out_ticks = 0; u64 out_ticks = 0;
if (same_thread && info_sub_id == 0xFFFFFFFFFFFFFFFF) { if (same_thread && info_sub_id == 0xFFFFFFFFFFFFFFFF) {
const u64 thread_ticks = current_thread->GetCpuTime(); const u64 thread_ticks = current_thread->GetCpuTime();
out_ticks = thread_ticks + (core_timing.GetClockTicks() - prev_ctx_ticks); out_ticks = thread_ticks + (core_timing.GetClockTicks() - prev_ctx_ticks);
} else if (same_thread && info_sub_id == system.Kernel().CurrentPhysicalCoreIndex()) { } else if (same_thread && info_sub_id == system.Kernel().CurrentPhysicalCoreIndex()) {
out_ticks = core_timing.GetClockTicks() - prev_ctx_ticks; 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(); R_SUCCEED();
} }
case InfoType::IdleTickCount: { case InfoType::IdleTickCount: {
// Verify the input handle is invalid.
R_UNLESS(handle == InvalidHandle, ResultInvalidHandle);
// Verify the requested core is valid. // Verify the requested core is valid.
const bool core_valid = const bool core_valid =
(info_sub_id == 0xFFFFFFFFFFFFFFFF) || (info_sub_id == 0xFFFFFFFFFFFFFFFF)
(info_sub_id == static_cast<u64>(system.Kernel().CurrentPhysicalCoreIndex())); || (info_sub_id == u64(system.Kernel().CurrentPhysicalCoreIndex()));
R_UNLESS(core_valid, ResultInvalidCombination); R_UNLESS(core_valid, ResultInvalidCombination);
// Verify the input handle is invalid.
R_UNLESS(handle == InvalidHandle, ResultInvalidHandle);
// Get the idle tick count. // Get the idle tick count.
*result = system.Kernel().CurrentScheduler()->GetIdleThread()->GetCpuTime(); *result = system.Kernel().CurrentScheduler()->GetIdleThread()->GetCpuTime();
R_SUCCEED(); 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: { case InfoType::MesosphereCurrentProcess: {
// Verify the input handle is invalid. // Verify the input handle is invalid.
R_UNLESS(handle == InvalidHandle, ResultInvalidHandle); 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(); KHandleTable& handle_table = current_process->GetHandleTable();
// Get a new handle for the current process. // Get a new handle for the current process.
Handle tmp; Handle tmp{};
R_TRY(handle_table.Add(system.Kernel(), std::addressof(tmp), current_process)); R_TRY(handle_table.Add(system.Kernel(), std::addressof(tmp), current_process));
// Set the output. // Set the output.
*result = tmp; *result = tmp;
// We succeeded.
R_SUCCEED(); R_SUCCEED();
} }
default: default:
+2 -1
View File
@@ -1387,7 +1387,8 @@ public:
{5, &ACC_U1::GetProfile, "GetProfile"}, {5, &ACC_U1::GetProfile, "GetProfile"},
{6, nullptr, "GetProfileDigest"}, {6, nullptr, "GetProfileDigest"},
{50, &ACC_U1::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"}, {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"}, {60, &ACC_U1::ListOpenContextStoredUsers, "ListOpenContextStoredUsers"},
{99, nullptr, "DebugActivateOpenContextRetention"}, {99, nullptr, "DebugActivateOpenContextRetention"},
{100, nullptr, "GetUserRegistrationNotifier"}, {100, nullptr, "GetUserRegistrationNotifier"},
+1
View File
@@ -154,6 +154,7 @@ enum class AppletMessage : u32 {
DetectLongPressingCaptureButton = 91, DetectLongPressingCaptureButton = 91,
AlbumScreenShotTaken = 92, AlbumScreenShotTaken = 92,
AlbumRecordingSaved = 93, AlbumRecordingSaved = 93,
StartupLogoDisappeared = 95,
}; };
enum class LibraryAppletMode : u32 { enum class LibraryAppletMode : u32 {
+1
View File
@@ -91,6 +91,7 @@ struct Applet {
// Common state // Common state
bool sleep_lock_enabled{}; bool sleep_lock_enabled{};
bool vr_mode_enabled{}; bool vr_mode_enabled{};
bool vr_mode_enabled_3d{};
bool lcd_backlight_off_enabled{}; bool lcd_backlight_off_enabled{};
APM::CpuBoostMode boost_mode{}; APM::CpuBoostMode boost_mode{};
bool request_exit_to_library_applet_at_execute_next_program_enabled{}; bool request_exit_to_library_applet_at_execute_next_program_enabled{};
@@ -73,20 +73,16 @@ Result DisplayLayerManager::CreateManagedDisplayLayer(u64* out_layer_id) {
R_TRY(m_manager_display_service->CreateManagedLayer( R_TRY(m_manager_display_service->CreateManagedLayer(
out_layer_id, 0, display_id, Service::AppletResourceUserId{m_process->GetProcessId()})); 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) { if (m_applet_id != AppletId::Application) {
(void)m_manager_display_service->SetLayerBlending(m_blending_enabled, *out_layer_id); (void)m_manager_display_service->SetLayerBlending(m_blending_enabled, *out_layer_id);
if (m_applet_id == AppletId::OverlayDisplay) { 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); (void)m_display_service->GetContainer()->SetLayerIsOverlay(*out_layer_id, true);
} else { } 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); m_managed_display_layers.emplace(*out_layer_id);
R_SUCCEED(); R_SUCCEED();
@@ -126,12 +122,11 @@ Result DisplayLayerManager::IsSystemBufferSharingEnabled() {
// Ensure the overlay layer is visible // Ensure the overlay layer is visible
m_manager_display_service->SetLayerVisibility(m_visible, m_system_shared_layer_id); 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); 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) { if (m_applet_id == AppletId::OverlayDisplay) {
initial_z = Overlay; initial_z = -1;
(void)m_display_service->GetContainer()->SetLayerIsOverlay(m_system_shared_layer_id, true); (void)m_display_service->GetContainer()->SetLayerIsOverlay(m_system_shared_layer_id, true);
} }
m_manager_display_service->SetLayerZIndex(initial_z, m_system_shared_layer_id); 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-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
@@ -73,8 +73,7 @@ Result IAllSystemAppletProxiesService::OpenLibraryAppletProxy(
Result IAllSystemAppletProxiesService::OpenOverlayAppletProxy( Result IAllSystemAppletProxiesService::OpenOverlayAppletProxy(
Out<SharedPointer<IOverlayAppletProxy>> out_overlay_applet_proxy, ClientProcessId pid, Out<SharedPointer<IOverlayAppletProxy>> out_overlay_applet_proxy, ClientProcessId pid,
InCopyHandle<Kernel::KProcess> process_handle, InCopyHandle<Kernel::KProcess> process_handle) {
InLargeData<AppletAttribute, BufferAttr_HipcMapAlias> attribute) {
LOG_WARNING(Service_AM, "called"); LOG_WARNING(Service_AM, "called");
if (const auto applet = this->GetAppletFromProcessId(pid); applet) { if (const auto applet = this->GetAppletFromProcessId(pid); applet) {
@@ -89,8 +88,7 @@ Result IAllSystemAppletProxiesService::OpenOverlayAppletProxy(
Result IAllSystemAppletProxiesService::OpenSystemApplicationProxy( Result IAllSystemAppletProxiesService::OpenSystemApplicationProxy(
Out<SharedPointer<IApplicationProxy>> out_system_application_proxy, ClientProcessId pid, Out<SharedPointer<IApplicationProxy>> out_system_application_proxy, ClientProcessId pid,
InCopyHandle<Kernel::KProcess> process_handle, InCopyHandle<Kernel::KProcess> process_handle) {
InLargeData<AppletAttribute, BufferAttr_HipcMapAlias> attribute) {
LOG_DEBUG(Service_AM, "called"); LOG_DEBUG(Service_AM, "called");
if (const auto applet = this->GetAppletFromProcessId(pid); applet) { 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-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
@@ -36,15 +36,13 @@ private:
InCopyHandle<Kernel::KProcess> process_handle, InCopyHandle<Kernel::KProcess> process_handle,
InLargeData<AppletAttribute, BufferAttr_HipcMapAlias> attribute); InLargeData<AppletAttribute, BufferAttr_HipcMapAlias> attribute);
Result OpenOverlayAppletProxy(Out<SharedPointer<IOverlayAppletProxy>> out_overlay_applet_proxy, Result OpenOverlayAppletProxy(Out<SharedPointer<IOverlayAppletProxy>> out_overlay_applet_proxy,
ClientProcessId pid, InCopyHandle<Kernel::KProcess> process_handle, ClientProcessId pid, InCopyHandle<Kernel::KProcess> process_handle);
InLargeData<AppletAttribute, BufferAttr_HipcMapAlias> attribute);
Result OpenLibraryAppletProxyOld( Result OpenLibraryAppletProxyOld(
Out<SharedPointer<ILibraryAppletProxy>> out_library_applet_proxy, ClientProcessId pid, Out<SharedPointer<ILibraryAppletProxy>> out_library_applet_proxy, ClientProcessId pid,
InCopyHandle<Kernel::KProcess> process_handle); InCopyHandle<Kernel::KProcess> process_handle);
Result OpenSystemApplicationProxy( Result OpenSystemApplicationProxy(
Out<SharedPointer<IApplicationProxy>> out_system_application_proxy, ClientProcessId pid, Out<SharedPointer<IApplicationProxy>> out_system_application_proxy, ClientProcessId pid,
InCopyHandle<Kernel::KProcess> process_handle, InCopyHandle<Kernel::KProcess> process_handle);
InLargeData<AppletAttribute, BufferAttr_HipcMapAlias> attribute);
Result GetSystemProcessCommonFunctions(); Result GetSystemProcessCommonFunctions();
Result GetAppletAlternativeFunctions(); Result GetAppletAlternativeFunctions();
@@ -39,7 +39,7 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system_, std::shared_ptr<Ap
{14, nullptr, "GetWakeupCount"}, //11.0.0+ {14, nullptr, "GetWakeupCount"}, //11.0.0+
{15, nullptr, "Unknown15"}, //19.0.0+ {15, nullptr, "Unknown15"}, //19.0.0+
{20, D<&ICommonStateGetter::PushToGeneralChannel>, "PushToGeneralChannel"}, {20, D<&ICommonStateGetter::PushToGeneralChannel>, "PushToGeneralChannel"},
{30, nullptr, "GetHomeButtonReaderLockAccessor"}, {30, D<&ICommonStateGetter::GetHomeButtonReaderLockAccessor>, "GetHomeButtonReaderLockAccessor"},
{31, D<&ICommonStateGetter::GetReaderLockAccessorEx>, "GetReaderLockAccessorEx"}, //2.0.0+ {31, D<&ICommonStateGetter::GetReaderLockAccessorEx>, "GetReaderLockAccessorEx"}, //2.0.0+
{32, D<&ICommonStateGetter::GetWriterLockAccessorEx>, "GetWriterLockAccessorEx"}, //7.0.0+ {32, D<&ICommonStateGetter::GetWriterLockAccessorEx>, "GetWriterLockAccessorEx"}, //7.0.0+
{40, nullptr, "GetCradleFwVersion"}, //2.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"}, {100, D<&ICommonStateGetter::SetHandlingHomeButtonShortPressedEnabled>, "SetHandlingHomeButtonShortPressedEnabled"},
{110, nullptr, "OpenMyGpuErrorHandler"}, {110, nullptr, "OpenMyGpuErrorHandler"},
{120, D<&ICommonStateGetter::GetAppletLaunchedHistory>, "GetAppletLaunchedHistory"}, //13.0.0+ {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"}, {200, D<&ICommonStateGetter::GetOperationModeSystemInfo>, "GetOperationModeSystemInfo"},
{300, D<&ICommonStateGetter::GetSettingsPlatformRegion>, "GetSettingsPlatformRegion"}, {300, D<&ICommonStateGetter::GetSettingsPlatformRegion>, "GetSettingsPlatformRegion"},
{400, nullptr, "ActivateMigrationService"}, {400, nullptr, "ActivateMigrationService"},
@@ -74,14 +74,17 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system_, std::shared_ptr<Ap
{501, nullptr, "SuppressDisablingSleepTemporarily"}, {501, nullptr, "SuppressDisablingSleepTemporarily"},
{502, nullptr, "IsSleepEnabled"}, {502, nullptr, "IsSleepEnabled"},
{503, nullptr, "IsDisablingSleepSuppressed"}, {503, nullptr, "IsDisablingSleepSuppressed"},
{600, nullptr, "Unknown600"}, //20.0.0+ {600, nullptr, "SetHidInputMagnificationForApplication"}, //20.0.0+
{610, D<&ICommonStateGetter::Unknown610>, "Unknown610"}, //21.0.0+ {610, D<&ICommonStateGetter::Unknown610>, "Unknown610"}, //21.0.0+
{611, D<&ICommonStateGetter::Unknown611>, "Unknown611"}, //22.0.0+ {611, D<&ICommonStateGetter::Unknown611>, "Unknown611"}, //22.0.0+
{900, D<&ICommonStateGetter::SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled>, "SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled"}, //11.0.0+ {900, D<&ICommonStateGetter::SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled>, "SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled"}, //11.0.0+
{910, nullptr, "GetLaunchRequiredTick"}, //17.0.0+ {910, nullptr, "GetLaunchRequiredTick"}, //17.0.0+
{1000, nullptr, "BeginVrMode3d"}, //19.0.0+ {1000, D<&ICommonStateGetter::BeginVrMode3d>, "BeginVrMode3d"}, //19.0.0+
{1001, nullptr, "EndVrMode3d"}, //19.0.0+ {1001, D<&ICommonStateGetter::EndVrMode3d>, "EndVrMode3d"}, //19.0.0+
{1002, nullptr, "IsVrModeEnabled3d"}, //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 // clang-format on
@@ -311,6 +314,12 @@ Result ICommonStateGetter::PerformSystemButtonPressingIfInFocus(SystemButtonType
R_SUCCEED(); 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) { Result ICommonStateGetter::GetOperationModeSystemInfo(Out<u32> out_operation_mode_system_info) {
LOG_WARNING(Service_AM, "(STUBBED) called"); LOG_WARNING(Service_AM, "(STUBBED) called");
*out_operation_mode_system_info = 0; *out_operation_mode_system_info = 0;
@@ -355,6 +364,12 @@ Result ICommonStateGetter::PushToGeneralChannel(SharedPointer<IStorage> storage)
R_SUCCEED(); 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) { Result ICommonStateGetter::SetHandlingHomeButtonShortPressedEnabled(bool enabled) {
LOG_DEBUG(Service_AM, "called, enabled={} applet_id={}", enabled, m_applet->applet_id); LOG_DEBUG(Service_AM, "called, enabled={} applet_id={}", enabled, m_applet->applet_id);
@@ -363,14 +378,58 @@ Result ICommonStateGetter::SetHandlingHomeButtonShortPressedEnabled(bool enabled
R_SUCCEED(); R_SUCCEED();
} }
Result ICommonStateGetter::Unknown610() { Result ICommonStateGetter::Unknown610(u64 unk) {
LOG_WARNING(Service_AM, "(STUBBED) called"); LOG_WARNING(Service_AM, "(STUBBED) called");
R_SUCCEED(); R_SUCCEED();
} }
Result ICommonStateGetter::Unknown611() { Result ICommonStateGetter::Unknown611(u8 unk) {
LOG_WARNING(Service_AM, "(STUBBED) called"); LOG_WARNING(Service_AM, "(STUBBED) called");
R_SUCCEED(); 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 } // namespace Service::AM
@@ -56,15 +56,23 @@ private:
Result GetDefaultDisplayResolution(Out<s32> out_width, Out<s32> out_height); Result GetDefaultDisplayResolution(Out<s32> out_width, Out<s32> out_height);
Result GetBuiltInDisplayType(Out<s32> out_display_type); Result GetBuiltInDisplayType(Out<s32> out_display_type);
Result PerformSystemButtonPressingIfInFocus(SystemButtonType type); Result PerformSystemButtonPressingIfInFocus(SystemButtonType type);
Result EnableStartupLogoDisappearedMessage();
Result GetOperationModeSystemInfo(Out<u32> out_operation_mode_system_info); Result GetOperationModeSystemInfo(Out<u32> out_operation_mode_system_info);
Result GetAppletLaunchedHistory(Out<s32> out_count, Result GetAppletLaunchedHistory(Out<s32> out_count,
OutArray<AppletId, BufferAttr_HipcMapAlias> out_applet_ids); OutArray<AppletId, BufferAttr_HipcMapAlias> out_applet_ids);
Result GetSettingsPlatformRegion(Out<Set::PlatformRegion> out_settings_platform_region); Result GetSettingsPlatformRegion(Out<Set::PlatformRegion> out_settings_platform_region);
Result SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled(); Result SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled();
Result PushToGeneralChannel(SharedPointer<IStorage> storage); // cmd 20 Result PushToGeneralChannel(SharedPointer<IStorage> storage); // cmd 20
Result GetHomeButtonReaderLockAccessor(Out<SharedPointer<ILockAccessor>> out_lock_accessor);
Result SetHandlingHomeButtonShortPressedEnabled(bool enabled); Result SetHandlingHomeButtonShortPressedEnabled(bool enabled);
Result Unknown610(); Result Unknown610(u64 unk);
Result Unknown611(); 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); void SetCpuBoostMode(HLERequestContext& ctx);
+5 -5
View File
@@ -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-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -26,17 +29,14 @@ IStorage::~IStorage() = default;
Result IStorage::Open(Out<SharedPointer<IStorageAccessor>> out_storage_accessor) { Result IStorage::Open(Out<SharedPointer<IStorageAccessor>> out_storage_accessor) {
LOG_DEBUG(Service_AM, "called"); LOG_DEBUG(Service_AM, "called");
R_UNLESS(m_impl->GetHandle() == nullptr, AM::ResultInvalidStorageType); R_UNLESS(m_impl->GetHandle() == nullptr, AM::ResultInvalidStorageType);
*out_storage_accessor = std::make_shared<IStorageAccessor>(system, m_impl); *out_storage_accessor = std::make_shared<IStorageAccessor>(system, m_impl);
R_SUCCEED(); R_SUCCEED();
} }
Result IStorage::OpenTransferStorage( Result IStorage::OpenTransferStorage(Out<SharedPointer<ITransferStorageAccessor>> out_transfer_storage_accessor) {
Out<SharedPointer<ITransferStorageAccessor>> out_transfer_storage_accessor) { LOG_DEBUG(Service_AM, "called");
R_UNLESS(m_impl->GetHandle() != nullptr, AM::ResultInvalidStorageType); R_UNLESS(m_impl->GetHandle() != nullptr, AM::ResultInvalidStorageType);
*out_transfer_storage_accessor = std::make_shared<ITransferStorageAccessor>(system, m_impl); *out_transfer_storage_accessor = std::make_shared<ITransferStorageAccessor>(system, m_impl);
R_SUCCEED(); R_SUCCEED();
} }
@@ -798,7 +798,7 @@ void LoopProcess(Core::System& system) {
const auto FileSystemProxyFactory = [&] { return std::make_shared<FSP_SRV>(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-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)); server_manager->RegisterNamedService("fsp-srv", std::move(FileSystemProxyFactory));
ServerManager::RunServer(std::move(server_manager)); ServerManager::RunServer(std::move(server_manager));
} }
+25 -7
View File
@@ -32,7 +32,6 @@ public:
RegisterHandlers(functions); RegisterHandlers(functions);
} }
~I2CSession() override = default; ~I2CSession() override = default;
Result Send(InBuffer<BufferAttr_HipcMapAlias> in_data, u32 transaction_option) { Result Send(InBuffer<BufferAttr_HipcMapAlias> in_data, u32 transaction_option) {
LOG_WARNING(Service, "(stubbed) topt={}", transaction_option); LOG_WARNING(Service, "(stubbed) topt={}", transaction_option);
R_THROW(ResultUnknown); R_THROW(ResultUnknown);
@@ -50,21 +49,40 @@ public:
: ServiceFramework{system_, "i2c"} : ServiceFramework{system_, "i2c"}
{ {
static const FunctionInfo functions[] = { static const FunctionInfo functions[] = {
{0, nullptr, "OpenSessionForDev"}, {0, C<&I2C::OpenSessionForDev>, "OpenSessionForDev"},
{1, C<&I2C::OpenSession>, "OpenSession"}, {1, C<&I2C::OpenSession>, "OpenSession"},
{2, nullptr, "HasDevice"}, {2, C<&I2C::HasDevice>, "HasDevice"},
{3, nullptr, "HasDeviceForDev"}, {3, C<&I2C::HasDeviceForDev>, "HasDeviceForDev"},
{4, nullptr, "OpenSession2"}, {4, C<&I2C::OpenSession2>, "OpenSession2"},
}; };
RegisterHandlers(functions); RegisterHandlers(functions);
} }
~I2C() override = default; ~I2C() override = default;
Result OpenSessionForDev(OutInterface<I2CSession> out_session, s32 bus_idx, u32 slave_address, u32 addressing_mode, u32 speed_mode) {
Result OpenSession(I2CDevice device, OutInterface<I2CSession> out_session) {
LOG_DEBUG(Service, "(stubbed)"); LOG_DEBUG(Service, "(stubbed)");
*out_session = std::make_shared<I2CSession>(system); *out_session = std::make_shared<I2CSession>(system);
R_SUCCEED(); 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) { 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
+21 -3
View File
@@ -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-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
@@ -7,7 +7,6 @@
#include "core/core.h" #include "core/core.h"
#include "core/hle/service/cmif_serialization.h" #include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/ldn/ldn.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/monitor_service.h"
#include "core/hle/service/ldn/sf_monitor_service.h" #include "core/hle/service/ldn/sf_monitor_service.h"
#include "core/hle/service/ldn/sf_service.h" #include "core/hle/service/ldn/sf_service.h"
@@ -17,6 +16,26 @@
namespace Service::LDN { 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> { class IMonitorServiceCreator final : public ServiceFramework<IMonitorServiceCreator> {
public: public:
explicit IMonitorServiceCreator(Core::System& system_) : ServiceFramework{system_, "ldn:m"} { explicit IMonitorServiceCreator(Core::System& system_) : ServiceFramework{system_, "ldn:m"} {
@@ -25,7 +44,6 @@ public:
{0, C<&IMonitorServiceCreator::CreateMonitorService>, "CreateMonitorService"} {0, C<&IMonitorServiceCreator::CreateMonitorService>, "CreateMonitorService"}
}; };
// clang-format on // clang-format on
RegisterHandlers(functions); RegisterHandlers(functions);
} }
@@ -139,7 +139,7 @@ IApplicationManagerInterface::IApplicationManagerInterface(Core::System& system_
{405, nullptr, "ListApplicationControlCacheEntryInfo"}, {405, nullptr, "ListApplicationControlCacheEntryInfo"},
{406, nullptr, "GetApplicationControlProperty"}, {406, nullptr, "GetApplicationControlProperty"},
{407, &IApplicationManagerInterface::ListApplicationTitle, "ListApplicationTitle"}, {407, &IApplicationManagerInterface::ListApplicationTitle, "ListApplicationTitle"},
{408, nullptr, "ListApplicationIcon"}, {408, &IApplicationManagerInterface::ListApplicationIcon, "ListApplicationIcon"},
{411, nullptr, "Unknown411"}, //19.0.0+ {411, nullptr, "Unknown411"}, //19.0.0+
{412, nullptr, "Unknown412"}, //19.0.0+ {412, nullptr, "Unknown412"}, //19.0.0+
{413, nullptr, "Unknown413"}, //19.0.0+ {413, nullptr, "Unknown413"}, //19.0.0+
@@ -894,4 +894,9 @@ void IApplicationManagerInterface::ListApplicationTitle(HLERequestContext& ctx)
IReadOnlyApplicationControlDataInterface(system).ListApplicationTitle(ctx); IReadOnlyApplicationControlDataInterface(system).ListApplicationTitle(ctx);
} }
void IApplicationManagerInterface::ListApplicationIcon(HLERequestContext& ctx) {
LOG_DEBUG(Service_NS, "called");
IReadOnlyApplicationControlDataInterface(system).ListApplicationIcon(ctx);
}
} // namespace Service::NS } // namespace Service::NS
@@ -78,6 +78,7 @@ public:
void PushApplicationRecord(HLERequestContext& ctx); void PushApplicationRecord(HLERequestContext& ctx);
void ListApplicationTitle(HLERequestContext& ctx); void ListApplicationTitle(HLERequestContext& ctx);
void ListApplicationIcon(HLERequestContext& ctx);
private: private:
KernelHelpers::ServiceContext service_context; KernelHelpers::ServiceContext service_context;
+40 -1
View File
@@ -10,7 +10,9 @@
#include "core/hle/service/ns/query_service.h" #include "core/hle/service/ns/query_service.h"
#include "core/hle/service/ns/service_getter_interface.h" #include "core/hle/service/ns/service_getter_interface.h"
#include "core/hle/service/ns/system_update_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" #include "core/hle/service/server_manager.h"
namespace Service::NS { 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) { void LoopProcess(Core::System& system) {
auto server_manager = std::make_unique<ServerManager>(system); auto server_manager = std::make_unique<ServerManager>(system);
@@ -10,13 +10,17 @@
#include <string> #include <string>
#include "common/stb.h" #include "common/stb.h"
#include "common/logging.h"
#include "common/settings.h" #include "common/settings.h"
#include "core/file_sys/control_metadata.h" #include "core/file_sys/control_metadata.h"
#include "core/file_sys/patch_manager.h" #include "core/file_sys/patch_manager.h"
#include "core/file_sys/vfs/vfs.h" #include "core/file_sys/vfs/vfs.h"
#include "core/hle/kernel/k_process.h" #include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_transfer_memory.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_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/language.h"
#include "core/hle/service/ns/ns_types.h" #include "core/hle/service/ns/ns_types.h"
#include "core/hle/service/ns/ns_results.h" #include "core/hle/service/ns/ns_results.h"
@@ -72,24 +76,26 @@ void SanitizeJPEGImageSize(std::vector<u8>& image) {
// IAsyncValue implementation for ListApplicationTitle // IAsyncValue implementation for ListApplicationTitle
// https://switchbrew.org/wiki/NS_services#ListApplicationTitle // https://switchbrew.org/wiki/NS_services#ListApplicationTitle
class IAsyncValueForListApplicationTitle final : public ServiceFramework<IAsyncValueForListApplicationTitle> { class IAsyncValue final : public ServiceFramework<IAsyncValue> {
public: public:
explicit IAsyncValueForListApplicationTitle(Core::System& system_, s32 offset, s32 size) explicit IAsyncValue(Core::System& system_, s32 offset, s32 size)
: ServiceFramework{system_, "IAsyncValue"}, service_context{system_, "IAsyncValue"}, : ServiceFramework{system_, "IAsyncValue"}
data_offset{offset}, data_size{size} { , service_context{system_, "IAsyncValue"}
, data_offset{offset}
, data_size{size}
{
static const FunctionInfo functions[] = { static const FunctionInfo functions[] = {
{0, &IAsyncValueForListApplicationTitle::GetSize, "GetSize"}, {0, D<&IAsyncValue::GetSize>, "GetSize"},
{1, &IAsyncValueForListApplicationTitle::Get, "Get"}, {1, D<&IAsyncValue::Get>, "Get"},
{2, &IAsyncValueForListApplicationTitle::Cancel, "Cancel"}, {2, D<&IAsyncValue::Cancel>, "Cancel"},
{3, &IAsyncValueForListApplicationTitle::GetErrorContext, "GetErrorContext"}, {3, D<&IAsyncValue::GetErrorContext>, "GetErrorContext"},
}; };
RegisterHandlers(functions); RegisterHandlers(functions);
completion_event = service_context.CreateEvent("IAsyncValue:Completion"); completion_event = service_context.CreateEvent("IAsyncValue:Completion");
completion_event->GetReadableEvent().Signal(system.Kernel()); completion_event->GetReadableEvent().Signal(system.Kernel());
} }
~IAsyncValueForListApplicationTitle() override { ~IAsyncValue() override {
service_context.CloseEvent(completion_event); service_context.CloseEvent(completion_event);
} }
@@ -98,35 +104,24 @@ public:
} }
private: private:
void GetSize(HLERequestContext& ctx) { Result GetSize(Out<s64> out_data_size) {
LOG_DEBUG(Service_NS, "called"); LOG_DEBUG(Service_NS, "called");
IPC::ResponseBuilder rb{ctx, 4}; *out_data_size = data_size;
rb.Push(ResultSuccess); R_SUCCEED();
rb.Push<s64>(data_size);
} }
Result Get(OutBuffer<BufferAttr_HipcMapAlias> out_data_offset) {
void Get(HLERequestContext& ctx) {
LOG_DEBUG(Service_NS, "called"); LOG_DEBUG(Service_NS, "called");
std::vector<u8> buffer(sizeof(s32)); std::memcpy(out_data_offset.data(), &data_offset, sizeof(s32));
std::memcpy(buffer.data(), &data_offset, sizeof(s32)); R_SUCCEED();
ctx.WriteBuffer(buffer);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
} }
Result Cancel() {
void Cancel(HLERequestContext& ctx) {
LOG_DEBUG(Service_NS, "called"); LOG_DEBUG(Service_NS, "called");
IPC::ResponseBuilder rb{ctx, 2}; R_SUCCEED();
rb.Push(ResultSuccess);
} }
Result GetErrorContext() {
void GetErrorContext(HLERequestContext& ctx) {
LOG_DEBUG(Service_NS, "called"); LOG_DEBUG(Service_NS, "called");
IPC::ResponseBuilder rb{ctx, 2}; R_SUCCEED();
rb.Push(ResultSuccess);
} }
KernelHelpers::ServiceContext service_context; KernelHelpers::ServiceContext service_context;
Kernel::KEvent* completion_event{}; Kernel::KEvent* completion_event{};
s32 data_offset; s32 data_offset;
@@ -144,6 +139,7 @@ IReadOnlyApplicationControlDataInterface::IReadOnlyApplicationControlDataInterfa
{3, nullptr, "ConvertLanguageCodeToApplicationLanguage"}, {3, nullptr, "ConvertLanguageCodeToApplicationLanguage"},
{4, nullptr, "SelectApplicationDesiredLanguage"}, {4, nullptr, "SelectApplicationDesiredLanguage"},
{5, D<&IReadOnlyApplicationControlDataInterface::GetApplicationControlData2>, "GetApplicationControlData"}, {5, D<&IReadOnlyApplicationControlDataInterface::GetApplicationControlData2>, "GetApplicationControlData"},
{10, &IReadOnlyApplicationControlDataInterface::ListApplicationIcon, "ListApplicationIcon"},
{13, &IReadOnlyApplicationControlDataInterface::ListApplicationTitle, "ListApplicationTitle"}, {13, &IReadOnlyApplicationControlDataInterface::ListApplicationTitle, "ListApplicationTitle"},
{19, D<&IReadOnlyApplicationControlDataInterface::GetApplicationControlData3>, "GetApplicationControlData"}, {19, D<&IReadOnlyApplicationControlDataInterface::GetApplicationControlData3>, "GetApplicationControlData"},
}; };
@@ -160,8 +156,7 @@ Result IReadOnlyApplicationControlDataInterface::GetApplicationControlData(
LOG_INFO(Service_NS, "called with control_source={}, application_id={:016X}", LOG_INFO(Service_NS, "called with control_source={}, application_id={:016X}",
application_control_source, application_id); application_control_source, application_id);
const FileSys::PatchManager pm{application_id, system.GetFileSystemController(), const FileSys::PatchManager pm{application_id, system.GetFileSystemController(), system.GetContentProvider()};
system.GetContentProvider()};
const auto control = pm.GetControlMetadata(); const auto control = pm.GetControlMetadata();
const auto size = out_buffer.size(); const auto size = out_buffer.size();
@@ -169,8 +164,7 @@ Result IReadOnlyApplicationControlDataInterface::GetApplicationControlData(
const auto total_size = sizeof(FileSys::RawNACP) + icon_size; const auto total_size = sizeof(FileSys::RawNACP) + icon_size;
if (size < total_size) { if (size < total_size) {
LOG_ERROR(Service_NS, "output buffer is too small! (actual={:016X}, expected_min=0x4000)", LOG_ERROR(Service_NS, "output buffer is too small! (actual={:016X}, expected_min=0x4000)", size);
size);
R_THROW(ResultUnknown); R_THROW(ResultUnknown);
} }
@@ -178,8 +172,7 @@ Result IReadOnlyApplicationControlDataInterface::GetApplicationControlData(
const auto bytes = control.first->GetRawBytes(); const auto bytes = control.first->GetRawBytes();
std::memcpy(out_buffer.data(), bytes.data(), bytes.size()); std::memcpy(out_buffer.data(), bytes.data(), bytes.size());
} else { } else {
LOG_WARNING(Service_NS, "missing NACP data for application_id={:016X}, defaulting to zero", LOG_WARNING(Service_NS, "missing NACP data for application_id={:016X}, defaulting to zero", application_id);
application_id);
std::memset(out_buffer.data(), 0, sizeof(FileSys::RawNACP)); std::memset(out_buffer.data(), 0, sizeof(FileSys::RawNACP));
} }
@@ -204,15 +197,12 @@ Result IReadOnlyApplicationControlDataInterface::GetApplicationDesiredLanguage(
// Convert to application language, get priority list // Convert to application language, get priority list
const auto application_language = ConvertToApplicationLanguage(language_code); const auto application_language = ConvertToApplicationLanguage(language_code);
if (application_language == std::nullopt) { if (application_language == std::nullopt) {
LOG_ERROR(Service_NS, "Could not convert application language! language_code={}", LOG_ERROR(Service_NS, "Could not convert application language! language_code={}", language_code);
language_code);
R_THROW(Service::NS::ResultApplicationLanguageNotFound); R_THROW(Service::NS::ResultApplicationLanguageNotFound);
} }
const auto priority_list = GetApplicationLanguagePriorityList(*application_language); const auto priority_list = GetApplicationLanguagePriorityList(*application_language);
if (!priority_list) { if (!priority_list) {
LOG_ERROR(Service_NS, LOG_ERROR(Service_NS, "Could not find application language priorities! application_language={}", *application_language);
"Could not find application language priorities! application_language={}",
*application_language);
R_THROW(Service::NS::ResultApplicationLanguageNotFound); R_THROW(Service::NS::ResultApplicationLanguageNotFound);
} }
@@ -256,8 +246,7 @@ Result IReadOnlyApplicationControlDataInterface::GetApplicationControlData2(
const auto nacp_size = sizeof(FileSys::RawNACP); const auto nacp_size = sizeof(FileSys::RawNACP);
if (size < nacp_size) { if (size < nacp_size) {
LOG_ERROR(Service_NS, "output buffer is too small! (actual={:016X}, expected_min={:08X})", LOG_ERROR(Service_NS, "output buffer is too small! (actual={:016X}, expected_min={:08X})", size, nacp_size);
size, nacp_size);
R_THROW(ResultUnknown); R_THROW(ResultUnknown);
} }
@@ -308,63 +297,81 @@ Result IReadOnlyApplicationControlDataInterface::GetApplicationControlData2(
R_SUCCEED(); R_SUCCEED();
} }
void IReadOnlyApplicationControlDataInterface::ListApplicationIcon(HLERequestContext& ctx) {
void IReadOnlyApplicationControlDataInterface::ListApplicationTitle(HLERequestContext& ctx) { LOG_WARNING(Service_NS, "(stubbed)");
/*
IPC::RequestParser rp{ctx};
auto control_source = rp.PopRaw<u8>();
rp.Skip(7, false);
auto transfer_memory_size = rp.Pop<u64>();
*/
const auto app_ids_buffer = ctx.ReadBuffer(); const auto app_ids_buffer = ctx.ReadBuffer();
const size_t app_count = app_ids_buffer.size() / sizeof(u64); const u64 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));
}
auto t_mem_obj = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(ctx.GetCopyHandle(0)); auto t_mem_obj = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(ctx.GetCopyHandle(0));
auto* t_mem = t_mem_obj.GetPointerUnsafe(); auto* t_mem = t_mem_obj.GetPointerUnsafe();
size_t out_length = 0;
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 && t_mem->GetOwner() != nullptr && app_count > 0) { if (t_mem != nullptr && t_mem->GetOwner() != nullptr && app_count > 0) {
auto& memory = t_mem->GetOwner()->GetMemory(); auto& memory = t_mem->GetOwner()->GetMemory();
const auto t_mem_address = t_mem->GetSourceAddress(); 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) { for (size_t i = 0; i < app_count; ++i) {
const u64 app_id = application_ids[i]; const u64 app_id = app_ids_buffer[i];
const FileSys::PatchManager pm{app_id, system.GetFileSystemController(), const FileSys::PatchManager pm{app_id, system.GetFileSystemController(), system.GetContentProvider()};
system.GetContentProvider()};
const auto control = pm.GetControlMetadata(); const auto control = pm.GetControlMetadata();
u64 full_size = control.second->GetSize();
FileSys::LanguageEntry entry{}; memory.WriteBlock(t_mem_address + out_length, &full_size, sizeof(u64));
if (control.first != nullptr) { out_length += sizeof(u64);
entry = control.first->GetLanguageEntry();
} }
// [list of raw icon data]
const size_t offset = i * title_entry_size; std::vector<u8> full_icon_data;
memory.WriteBlock(t_mem_address + offset, &entry, title_entry_size); 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;
} }
} }
}
auto async_value = std::make_shared<IAsyncValueForListApplicationTitle>( auto async_value = std::make_shared<IAsyncValue>(system, 0, s32(out_length));
system, data_offset, static_cast<s32>(total_data_size));
IPC::ResponseBuilder rb{ctx, 2, 1, 1}; IPC::ResponseBuilder rb{ctx, 2, 1, 1};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushCopyObjects(ctx, async_value->ReadableEvent()); rb.PushCopyObjects(ctx, async_value->ReadableEvent());
rb.PushIpcInterface(ctx, std::move(async_value)); rb.PushIpcInterface(ctx, std::move(async_value));
} }
Result IReadOnlyApplicationControlDataInterface::GetApplicationControlData3( void IReadOnlyApplicationControlDataInterface::ListApplicationTitle(HLERequestContext& ctx) {
OutBuffer<BufferAttr_HipcMapAlias> out_buffer, Out<u32> out_flags_a, Out<u32> out_flags_b, const auto app_ids_buffer = ctx.ReadBuffer();
Out<u32> out_actual_size, ApplicationControlSource application_control_source, u8 flag1, u8 flag2, u64 application_id) { 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}", LOG_INFO(Service_NS, "called with control_source={}, flags=({:02X},{:02X}), application_id={:016X}",
application_control_source, flag1, flag2, application_id); application_control_source, flag1, flag2, application_id);
@@ -34,6 +34,7 @@ public:
u8 flag1, u8 flag1,
u8 flag2, u8 flag2,
u64 application_id); u64 application_id);
void ListApplicationIcon(HLERequestContext& ctx);
void ListApplicationTitle(HLERequestContext& ctx); void ListApplicationTitle(HLERequestContext& ctx);
Result GetApplicationControlData3( Result GetApplicationControlData3(
OutBuffer<BufferAttr_HipcMapAlias> out_buffer, 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-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
@@ -16,10 +16,8 @@ IReadOnlyApplicationRecordInterface::IReadOnlyApplicationRecordInterface(Core::S
static const FunctionInfo functions[] = { static const FunctionInfo functions[] = {
{0, D<&IReadOnlyApplicationRecordInterface::HasApplicationRecord>, "HasApplicationRecord"}, {0, D<&IReadOnlyApplicationRecordInterface::HasApplicationRecord>, "HasApplicationRecord"},
{1, nullptr, "NotifyApplicationFailure"}, {1, nullptr, "NotifyApplicationFailure"},
{2, D<&IReadOnlyApplicationRecordInterface::IsDataCorruptedResult>, {2, D<&IReadOnlyApplicationRecordInterface::IsDataCorruptedResult>, "IsDataCorruptedResult"},
"IsDataCorruptedResult"}, {3, D<&IReadOnlyApplicationRecordInterface::ListApplicationRecord>, "ListApplicationRecord"},
{3, D<&IReadOnlyApplicationRecordInterface::ListApplicationRecord>,
"ListApplicationRecord"},
}; };
// clang-format on // 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
@@ -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-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -30,15 +33,15 @@ struct DeviceSettings {
INSERT_PADDING_BYTES(0x20); // Reserved INSERT_PADDING_BYTES(0x20); // Reserved
// nn::settings::system::ConsoleSixAxisSensorAccelerationBias // 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 // 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 // nn::settings::system::ConsoleSixAxisSensorAccelerationGain
std::array<u8, 0x24> console_six_axis_sensor_acceleration_gain; std::array<u8, 0x24> console_six_axis_sensor_acceleration_gain;
// nn::settings::system::ConsoleSixAxisSensorAngularVelocityGain // nn::settings::system::ConsoleSixAxisSensorAngularVelocityGain
std::array<u8, 0x24> console_six_axis_sensor_angular_velocity_gain; std::array<u8, 0x24> console_six_axis_sensor_angular_velocity_gain;
// nn::settings::system::ConsoleSixAxisSensorAngularVelocityTimeBias // 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 // nn::settings::system::ConsoleSixAxisSensorAngularAcceleration
std::array<u8, 0x24> console_six_axis_sensor_angular_acceleration; 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-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
@@ -153,15 +153,15 @@ struct SystemSettings {
INSERT_PADDING_BYTES(0x7FF8); // Reserved INSERT_PADDING_BYTES(0x7FF8); // Reserved
// nn::settings::system::ConsoleSixAxisSensorAccelerationBias // 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 // 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 // nn::settings::system::ConsoleSixAxisSensorAccelerationGain
std::array<u8, 0x24> console_six_axis_sensor_acceleration_gain; std::array<u8, 0x24> console_six_axis_sensor_acceleration_gain;
// nn::settings::system::ConsoleSixAxisSensorAngularVelocityGain // nn::settings::system::ConsoleSixAxisSensorAngularVelocityGain
std::array<u8, 0x24> console_six_axis_sensor_angular_velocity_gain; std::array<u8, 0x24> console_six_axis_sensor_angular_velocity_gain;
// nn::settings::system::ConsoleSixAxisSensorAngularVelocityTimeBias // 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 // nn::settings::system::ConsoleSixAxisSensorAngularAcceleration
std::array<u8, 0x24> console_six_axis_sensor_angular_velocity_acceleration; std::array<u8, 0x24> console_six_axis_sensor_angular_velocity_acceleration;
INSERT_PADDING_BYTES(0x70); // Reserved INSERT_PADDING_BYTES(0x70); // Reserved
-18
View File
@@ -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
-20
View File
@@ -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
+12 -1
View File
@@ -13,7 +13,6 @@
#include "core/hle/api_version.h" #include "core/hle/api_version.h"
#include "core/hle/service/ipc_helpers.h" #include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/server_manager.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.h"
#include "core/hle/service/spl/spl_module.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) { void LoopProcess(Core::System& system) {
auto server_manager = std::make_unique<ServerManager>(system); auto server_manager = std::make_unique<ServerManager>(system);
auto module = std::make_shared<Module>(); auto module = std::make_shared<Module>();
@@ -13,6 +13,7 @@
#include "core/hle/service/vi/manager_display_service.h" #include "core/hle/service/vi/manager_display_service.h"
#include "core/hle/service/vi/system_display_service.h" #include "core/hle/service/vi/system_display_service.h"
#include "core/hle/service/vi/vi_results.h" #include "core/hle/service/vi/vi_results.h"
#include "service_creator.h"
namespace Service::VI { namespace Service::VI {
@@ -38,6 +39,7 @@ IApplicationDisplayService::IApplicationDisplayService(Core::System& system_,
{2031, C<&IApplicationDisplayService::DestroyStrayLayer>, "DestroyStrayLayer"}, {2031, C<&IApplicationDisplayService::DestroyStrayLayer>, "DestroyStrayLayer"},
{2101, C<&IApplicationDisplayService::SetLayerScalingMode>, "SetLayerScalingMode"}, {2101, C<&IApplicationDisplayService::SetLayerScalingMode>, "SetLayerScalingMode"},
{2102, C<&IApplicationDisplayService::ConvertScalingMode>, "ConvertScalingMode"}, {2102, C<&IApplicationDisplayService::ConvertScalingMode>, "ConvertScalingMode"},
{2103, C<&IApplicationDisplayService::Cmd2103>, "Cmd2103"},
{2450, C<&IApplicationDisplayService::GetIndirectLayerImageMap>, "GetIndirectLayerImageMap"}, {2450, C<&IApplicationDisplayService::GetIndirectLayerImageMap>, "GetIndirectLayerImageMap"},
{2451, nullptr, "GetIndirectLayerImageCropMap"}, {2451, nullptr, "GetIndirectLayerImageCropMap"},
{2460, C<&IApplicationDisplayService::GetIndirectLayerImageRequiredMemoryInfo>, "GetIndirectLayerImageRequiredMemoryInfo"}, {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( Result IApplicationDisplayService::GetIndirectLayerImageMap(
Out<u64> out_size, Out<u64> out_stride, Out<u64> out_size, Out<u64> out_stride,
OutBuffer<BufferAttr_HipcMapTransferAllowsNonSecure | BufferAttr_HipcMapAlias> out_buffer, OutBuffer<BufferAttr_HipcMapTransferAllowsNonSecure | BufferAttr_HipcMapAlias> out_buffer,
@@ -64,6 +64,7 @@ public:
Result GetDisplayVsyncEvent(OutCopyHandle<Kernel::KReadableEvent> out_vsync_event, Result GetDisplayVsyncEvent(OutCopyHandle<Kernel::KReadableEvent> out_vsync_event,
u64 display_id); u64 display_id);
Result ConvertScalingMode(Out<ConvertedScaleMode> out_scaling_mode, NintendoScaleMode mode); Result ConvertScalingMode(Out<ConvertedScaleMode> out_scaling_mode, NintendoScaleMode mode);
Result Cmd2103(Out<std::array<u8, 0x18>> out_unk18);
Result GetIndirectLayerImageMap( Result GetIndirectLayerImageMap(
Out<u64> out_size, Out<u64> out_stride, Out<u64> out_size, Out<u64> out_stride,
OutBuffer<BufferAttr_HipcMapTransferAllowsNonSecure | BufferAttr_HipcMapAlias> out_buffer, 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. // Map into process.
Common::ProcessAddress map_address{}; Common::ProcessAddress map_address{};
R_TRY(MapSharedBufferIntoProcessAddressSpace(std::addressof(map_address), m_buffer_page_group, 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. // Create new session.
auto [it, was_emplaced] = m_sessions.emplace(aruid, SharedBufferSession{}); auto [it, was_emplaced] = m_sessions.emplace(aruid, SharedBufferSession{});
auto& session = it->second; auto& session = it->second;
session.presentation_slot_base = slot_base;
auto& container = m_nvdrv->GetContainer(); auto& container = m_nvdrv->GetContainer();
session.session_id = container.OpenSession(owner_process); session.session_id = container.OpenSession(owner_process);
+42 -39
View File
@@ -101,8 +101,10 @@ struct Memory::Impl {
} }
u64 protect_bytes = 0, protect_begin = 0; 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) { 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) { switch (page_type) {
case Common::PageType::RasterizerCachedMemory: case Common::PageType::RasterizerCachedMemory:
if (protect_bytes > 0) { if (protect_bytes > 0) {
@@ -123,16 +125,14 @@ struct Memory::Impl {
} }
[[nodiscard]] u8* GetPointerFromRasterizerCachedMemory(u64 vaddr) const { [[nodiscard]] u8* GetPointerFromRasterizerCachedMemory(u64 vaddr) const {
Common::PhysicalAddress const paddr = current_page_table->entries[vaddr >> YUZU_PAGEBITS].addr; if (u64 paddr = current_page_table->entries[vaddr >> YUZU_PAGEBITS].Pointer(true); paddr)
if (paddr) return reinterpret_cast<u8*>(paddr) + vaddr;
return system.DeviceMemory().GetPointer<u8>(paddr + vaddr);
return {}; return {};
} }
[[nodiscard]] u8* GetPointerFromDebugMemory(u64 vaddr) const { [[nodiscard]] u8* GetPointerFromDebugMemory(u64 vaddr) const {
const Common::PhysicalAddress paddr = current_page_table->entries[vaddr >> YUZU_PAGEBITS].addr; if (u64 paddr = current_page_table->entries[vaddr >> YUZU_PAGEBITS].Pointer(true); paddr)
if (paddr != 0) return reinterpret_cast<u8*>(paddr) + vaddr;
return system.DeviceMemory().GetPointer<u8>(paddr + vaddr);
return {}; return {};
} }
@@ -243,10 +243,12 @@ struct Memory::Impl {
std::size_t page_index = addr >> YUZU_PAGEBITS; std::size_t page_index = addr >> YUZU_PAGEBITS;
std::size_t page_offset = addr & YUZU_PAGEMASK; std::size_t page_offset = addr & YUZU_PAGEMASK;
bool user_accessible = true; bool user_accessible = true;
current_page_table->entries.CommitRegion(page_index, page_index + (size >> YUZU_PAGEBITS) + 1);
while (remaining_size != 0) { while (remaining_size != 0) {
const std::size_t copy_amount = (std::min)(std::size_t(YUZU_PAGESIZE) - page_offset, remaining_size); 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 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) { switch (type) {
case Common::PageType::Unmapped: { case Common::PageType::Unmapped: {
user_accessible = false; 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 { [[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 { [[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) { 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. // The region is at a granularity of CPU pages.
const u64 num_pages = ((vaddr + size - 1) >> YUZU_PAGEBITS) - (vaddr >> YUZU_PAGEBITS) + 1; 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) { 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) { if (debug) {
// Switch page type to debug if now debug // Switch page type to debug if now debug
switch (page_type) { switch (type) {
case Common::PageType::Unmapped: case Common::PageType::Unmapped:
ASSERT(false && "Attempted to mark unmapped pages as debug"); ASSERT(false && "Attempted to mark unmapped pages as debug");
break; break;
@@ -417,14 +422,14 @@ struct Memory::Impl {
// Page is already marked. // Page is already marked.
break; break;
case Common::PageType::Memory: case Common::PageType::Memory:
current_page_table->entries[vaddr >> YUZU_PAGEBITS].ptr.Store(0, Common::PageType::DebugMemory); entry.MarkDebug(pointer, block);
break; break;
default: default:
UNREACHABLE(); UNREACHABLE();
} }
} else { } else {
// Switch page type to non-debug if now non-debug // Switch page type to non-debug if now non-debug
switch (page_type) { switch (type) {
case Common::PageType::Unmapped: case Common::PageType::Unmapped:
ASSERT(false && "Attempted to mark unmapped pages as non-debug"); ASSERT(false && "Attempted to mark unmapped pages as non-debug");
break; break;
@@ -433,8 +438,7 @@ struct Memory::Impl {
// Don't mess with already non-debug or rasterizer memory. // Don't mess with already non-debug or rasterizer memory.
break; break;
case Common::PageType::DebugMemory: { case Common::PageType::DebugMemory: {
u8* const pointer = GetPointerFromDebugMemory(vaddr & ~YUZU_PAGEMASK); entry.Store(false, Common::PageType::Memory, block, pointer);
current_page_table->entries[vaddr >> YUZU_PAGEBITS].ptr.Store(uintptr_t(pointer) - (vaddr & ~YUZU_PAGEMASK), Common::PageType::Memory);
break; break;
} }
default: default:
@@ -466,8 +470,10 @@ struct Memory::Impl {
// is different). This assumes the specified GPU address region is contiguous as well. // 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; 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) { 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) { if (cached) {
// Switch page type to cached if now cached // Switch page type to cached if now cached
switch (page_type) { switch (page_type) {
@@ -477,7 +483,7 @@ struct Memory::Impl {
break; break;
case Common::PageType::DebugMemory: case Common::PageType::DebugMemory:
case Common::PageType::Memory: case Common::PageType::Memory:
current_page_table->entries[vaddr >> YUZU_PAGEBITS].ptr.Store(0, Common::PageType::RasterizerCachedMemory); entry.MarkRasterizerCached();
break; break;
case Common::PageType::RasterizerCachedMemory: case Common::PageType::RasterizerCachedMemory:
// There can be more than one GPU region mapped per CPU region, so it's common // 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. // that this area is already unmarked as cached.
break; break;
case Common::PageType::RasterizerCachedMemory: { 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 // 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 // pagetable after unmapping a VMA. In that case the underlying VMA will no
// longer exist, and we should just leave the pagetable entry blank. // 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 { } 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; break;
} }
@@ -539,22 +545,18 @@ struct Memory::Impl {
ASSERT_MSG(type != Common::PageType::Memory, ASSERT_MSG(type != Common::PageType::Memory,
"Mapping memory page without a pointer @ {:016x}", base * YUZU_PAGESIZE); "Mapping memory page without a pointer @ {:016x}", base * YUZU_PAGESIZE);
while (base != end) { page_table.entries.ZeroRegion(base, end);
page_table.entries[base].ptr.Store(0, type);
page_table.entries[base].addr = 0;
page_table.entries[base].block = 0;
base += 1;
}
} else { } else {
auto orig_base = base; auto current_block = block_count.fetch_add(1, std::memory_order_relaxed);
while (base != end) { ASSERT(current_block != 65535);
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;
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"); "memory mapping base yield a nullptr within the table");
base += 1; base += 1;
@@ -569,11 +571,11 @@ struct Memory::Impl {
vaddr &= 0xffffffffffffULL; vaddr &= 0xffffffffffffULL;
if (AddressSpaceContains(*current_page_table, vaddr, 1)) [[likely]] { if (AddressSpaceContains(*current_page_table, vaddr, 1)) [[likely]] {
// Avoid adding any extra logic to this fast-path block // Avoid adding any extra logic to this fast-path block
const uintptr_t raw_pointer = current_page_table->entries[vaddr >> YUZU_PAGEBITS].ptr.Raw(); const auto raw = current_page_table->entries[vaddr >> YUZU_PAGEBITS].Raw();
if (const uintptr_t pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) [[likely]] { if (auto pointer = Common::PageTable::PageEntryData::ExtractPointer(raw); pointer) [[likely]] {
return reinterpret_cast<u8*>(pointer + vaddr); return reinterpret_cast<u8*>(pointer + vaddr);
} else { } else {
switch (Common::PageTable::PageInfo::ExtractType(raw_pointer)) { switch (static_cast<Common::PageType>(raw.type)) {
case Common::PageType::Memory: case Common::PageType::Memory:
ASSERT_MSG(false, "Mapped memory page without a pointer @ {:#016x}", vaddr); ASSERT_MSG(false, "Mapped memory page without a pointer @ {:#016x}", vaddr);
return nullptr; return nullptr;
@@ -773,6 +775,7 @@ struct Memory::Impl {
#else #else
Common::HostMemory* host_buffer{}; Common::HostMemory* host_buffer{};
#endif #endif
std::atomic<u16> block_count = 0;
}; };
Memory::Memory(Core::System& system_) : system{system_} { 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()) { if (page >= page_table.entries.size()) {
return false; 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 || return pointer != 0 || type == Common::PageType::RasterizerCachedMemory ||
type == Common::PageType::DebugMemory; type == Common::PageType::DebugMemory;
} }
+30 -2
View File
@@ -74,6 +74,7 @@ add_library(dynarmic STATIC
interface/halt_reason.h interface/halt_reason.h
interface/exclusive_monitor.h interface/exclusive_monitor.h
interface/optimization_flags.h interface/optimization_flags.h
interface/halt_reason.h
ir/acc_type.h ir/acc_type.h
ir/basic_block.cpp ir/basic_block.cpp
ir/basic_block.h 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 # Newer versions of xbyak (>= 7.25.0) have stricter checks that currently
# fail in dynarmic # fail in dynarmic
target_compile_definitions(dynarmic PRIVATE XBYAK_STRICT_CHECK_MEM_REG_SIZE=0) target_compile_definitions(dynarmic PRIVATE XBYAK_STRICT_CHECK_MEM_REG_SIZE=0)
target_compile_definitions(dynarmic PRIVATE XBYAK_OLD_DISP_CHECK=1) target_compile_definitions(dynarmic PRIVATE XBYAK_OLD_DISP_CHECK=1)
target_link_libraries(dynarmic PRIVATE xbyak::xbyak) target_link_libraries(dynarmic PRIVATE xbyak::xbyak)
target_architecture_specific_sources(dynarmic "x86_64" target_architecture_specific_sources(dynarmic "x86_64"
backend/x64/abi.cpp backend/x64/abi.cpp
backend/x64/abi.h backend/x64/abi.h
@@ -294,6 +293,35 @@ if ("riscv64" IN_LIST ARCHITECTURE)
) )
message(WARNING "TODO: Incomplete frontend for this host architecture") message(WARNING "TODO: Incomplete frontend for this host architecture")
endif() 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) if ("loongarch64" IN_LIST ARCHITECTURE)
target_link_libraries(dynarmic PRIVATE lagoon::lagoon) 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_pointer = std::bit_cast<u64>(conf.page_table),
.page_table_address_space_bits = 32, .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_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, .silently_mirror_page_table = true,
.absolute_offset_page_table = conf.absolute_offset_page_table, .absolute_offset_page_table = conf.absolute_offset_page_table,
.detect_misaligned_access_via_page_table = conf.detect_misaligned_access_via_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_pointer = std::bit_cast<u64>(conf.page_table),
.page_table_address_space_bits = conf.page_table_address_space_bits, .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_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, .silently_mirror_page_table = conf.silently_mirror_page_table,
.absolute_offset_page_table = conf.absolute_offset_page_table, .absolute_offset_page_table = conf.absolute_offset_page_table,
.detect_misaligned_access_via_page_table = conf.detect_misaligned_access_via_page_table, .detect_misaligned_access_via_page_table = conf.detect_misaligned_access_via_page_table,
@@ -128,8 +128,10 @@ struct EmitConfig {
// Page table // Page table
u64 page_table_pointer; u64 page_table_pointer;
std::size_t page_table_address_space_bits; 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::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 silently_mirror_page_table;
bool absolute_offset_page_table; bool absolute_offset_page_table;
u8 detect_misaligned_access_via_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> // load x0 = *<(u8*)pagetable + index>
code.LDR(Xscratch0, Xpagetable, Xscratch0); code.LDR(Xscratch0, Xpagetable, Xscratch0);
if (ctx.conf.page_table_pointer_mask_bits != 0) { if (ctx.conf.page_table_marked_bit) {
const u64 mask = u64(~u64(0)) << ctx.conf.page_table_pointer_mask_bits; // check for marked bit
code.AND(Xscratch0, Xscratch0, mask); 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); code.CBZ(Xscratch0, *fallback);
@@ -17,7 +17,7 @@
#if defined(ARCHITECTURE_x86_64) #if defined(ARCHITECTURE_x86_64)
namespace Dynarmic::Backend::X64 { namespace Dynarmic::Backend::X64 {
class BlockOfCode; class BlockOfCode;
} // namespace Dynarmic::Backend::X64 }
#elif defined(ARCHITECTURE_arm64) #elif defined(ARCHITECTURE_arm64)
namespace oaknut { namespace oaknut {
class CodeBlock; class CodeBlock;
@@ -30,6 +30,10 @@ class CodeBlock;
namespace Dynarmic::Backend::LoongArch64 { namespace Dynarmic::Backend::LoongArch64 {
class CodeBlock; class CodeBlock;
} // namespace Dynarmic::Backend::LoongArch64 } // namespace Dynarmic::Backend::LoongArch64
#elif defined(ARCHITECTURE_powerpc64)
namespace Dynarmic::Backend::PPC64 {
class CodeBlock;
}
#else #else
# error "Invalid architecture" # error "Invalid architecture"
#endif #endif
@@ -53,6 +57,9 @@ struct FakeCall {
struct FakeCall { struct FakeCall {
u64 call_pc; u64 call_pc;
}; };
#elif defined(ARCHITECTURE_powerpc64)
struct FakeCall {
};
#else #else
# error "Invalid architecture" # error "Invalid architecture"
#endif #endif
@@ -70,6 +77,8 @@ public:
void Register(RV64::CodeBlock& mem, std::size_t mem_size); void Register(RV64::CodeBlock& mem, std::size_t mem_size);
#elif defined(ARCHITECTURE_loongarch64) #elif defined(ARCHITECTURE_loongarch64)
void Register(LoongArch64::CodeBlock& mem, std::size_t mem_size); 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 #else
# error "Invalid architecture" # error "Invalid architecture"
#endif #endif
@@ -17,24 +17,21 @@ ExceptionHandler::ExceptionHandler() = default;
ExceptionHandler::~ExceptionHandler() = default; ExceptionHandler::~ExceptionHandler() = default;
#if defined(ARCHITECTURE_x86_64) #if defined(ARCHITECTURE_x86_64)
void ExceptionHandler::Register(X64::BlockOfCode&) { void ExceptionHandler::Register(X64::BlockOfCode&)
// Do nothing
}
#elif defined(ARCHITECTURE_arm64) #elif defined(ARCHITECTURE_arm64)
void ExceptionHandler::Register(oaknut::CodeBlock&, std::size_t) { void ExceptionHandler::Register(oaknut::CodeBlock&, std::size_t)
// Do nothing
}
#elif defined(ARCHITECTURE_riscv64) #elif defined(ARCHITECTURE_riscv64)
void ExceptionHandler::Register(RV64::CodeBlock&, std::size_t) { void ExceptionHandler::Register(RV64::CodeBlock&, std::size_t)
// Do nothing
}
#elif defined(ARCHITECTURE_loongarch64) #elif defined(ARCHITECTURE_loongarch64)
void ExceptionHandler::Register(LoongArch64::CodeBlock&, std::size_t) { void ExceptionHandler::Register(LoongArch64::CodeBlock&, std::size_t)
// Do nothing #elif defined(ARCHITECTURE_powerpc64)
} void ExceptionHandler::Register(PPC64::CodeBlock&, std::size_t)
#else #else
# error "Invalid architecture" # error "Invalid architecture"
#endif #endif
{
// Do nothing
}
bool ExceptionHandler::SupportsFastmem() const noexcept { bool ExceptionHandler::SupportsFastmem() const noexcept {
return false; return false;
@@ -11,6 +11,7 @@
#include <algorithm> #include <algorithm>
#include <bit> #include <bit>
#include <cstring> #include <cstring>
#include <algorithm>
#include <functional> #include <functional>
#include <memory> #include <memory>
#include <mutex> #include <mutex>
@@ -35,6 +36,8 @@
# include "dynarmic/backend/riscv64/code_block.h" # include "dynarmic/backend/riscv64/code_block.h"
#elif defined(ARCHITECTURE_loongarch64) #elif defined(ARCHITECTURE_loongarch64)
# include "dynarmic/backend/loongarch64/code_block.h" # include "dynarmic/backend/loongarch64/code_block.h"
#elif defined(ARCHITECTURE_powerpc64)
# include "dynarmic/backend/ppc64/code_block.h"
#else #else
# error "Invalid architecture" # error "Invalid architecture"
#endif #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) { void ExceptionHandler::Register(LoongArch64::CodeBlock& mem, std::size_t size) {
impl = std::make_unique<Impl>(std::bit_cast<u64>(mem.ptr<u64>()), 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 #else
# error "Invalid architecture" # error "Invalid architecture"
#endif #endif
@@ -0,0 +1,32 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <powah_emit.hpp>
#include <ankerl/unordered_dense.h>
#include "dynarmic/backend/ppc64/code_block.h"
#include "dynarmic/backend/ppc64/emit_ppc64.h"
#include "dynarmic/frontend/A32/a32_location_descriptor.h"
#include "dynarmic/interface/A32/a32.h"
#include "dynarmic/interface/A32/config.h"
#include "dynarmic/ir/terminal.h"
#include "dynarmic/ir/basic_block.h"
namespace Dynarmic::Backend::PPC64 {
struct A32JitState {
std::array<u32, 16> regs{};
alignas(16) std::array<u32, 64> ext_regs{};
u32 upper_location_descriptor;
u32 exclusive_state = 0;
u32 cpsr_nzcv = 0;
u32 fpscr = 0;
u32 check_bit = 0;
IR::LocationDescriptor GetLocationDescriptor() const {
return IR::LocationDescriptor{regs[15] | (u64(upper_location_descriptor) << 32)};
}
};
} // namespace Dynarmic::Backend::RV64
@@ -0,0 +1,250 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include <memory>
#include <mutex>
#include <boost/icl/interval_set.hpp>
#include "common/assert.h"
#include "common/common_types.h"
#include "dynarmic/frontend/A32/a32_location_descriptor.h"
#include "dynarmic/frontend/A32/translate/a32_translate.h"
#include "dynarmic/frontend/A32/FPSCR.h"
#include "dynarmic/interface/A32/config.h"
#include "dynarmic/backend/ppc64/a32_core.h"
#include "dynarmic/common/atomic.h"
#include "dynarmic/ir/opt_passes.h"
#include "dynarmic/interface/A32/a32.h"
namespace Dynarmic::A32 {
using namespace Dynarmic::Backend::PPC64;
using CodePtr = std::uint32_t*;
struct A32AddressSpace final {
explicit A32AddressSpace(const A32::UserConfig& conf)
: conf(conf)
, cb(conf.code_cache_size)
, as(cb.ptr<u8*>(), conf.code_cache_size) {
}
CodePtr GetOrEmit(IR::LocationDescriptor desc) {
if (auto const it = block_entries.find(desc.Value()); it != block_entries.end())
return it->second;
ir_block.Reset(A32::LocationDescriptor{desc});
A32::Translate(ir_block, A32::LocationDescriptor{desc}, conf.callbacks, {conf.arch_version, conf.define_unpredictable_behaviour, conf.hook_hint_instructions});
Optimization::Optimize(ir_block, conf, {});
const EmittedBlockInfo block_info = Emit(std::move(ir_block));
block_infos.insert_or_assign(desc.Value(), block_info);
block_entries.insert_or_assign(desc.Value(), block_info.entry_point);
return block_info.entry_point;
}
void ClearCache() {
block_entries.clear();
block_infos.clear();
}
EmittedBlockInfo Emit(IR::Block block) {
EmittedBlockInfo block_info = EmitPPC64(as, std::move(block), {
.enable_cycle_counting = conf.enable_cycle_counting,
.always_little_endian = conf.always_little_endian,
.a64_variant = false
});
Link(block_info);
return block_info;
}
void Link(EmittedBlockInfo& block_info) {
//UNREACHABLE();
}
IR::Block ir_block = {LocationDescriptor(0, PSR(0), FPSCR(0), false)};
const A32::UserConfig conf;
CodeBlock cb;
powah::Context as;
ankerl::unordered_dense::map<u64, CodePtr> block_entries;
ankerl::unordered_dense::map<u64, EmittedBlockInfo> block_infos;
};
struct A32Core final {
static HaltReason Run(A32AddressSpace& process, A32JitState& thread_ctx, volatile u32* halt_reason) {
auto const loc = thread_ctx.GetLocationDescriptor();
auto const entry = process.GetOrEmit(loc);
using CodeFn = HaltReason (*)(A32AddressSpace*, A32JitState*, volatile u32*, void*);
return (CodeFn(entry))(&process, &thread_ctx, halt_reason, reinterpret_cast<void*>(&A32Core::Run));
}
};
struct Jit::Impl final {
Impl(Jit* jit_interface, A32::UserConfig conf)
: conf(conf)
, current_address_space(conf)
, jit_interface(jit_interface) {}
HaltReason Run() {
ASSERT(!is_executing);
is_executing = false;
HaltReason hr = A32Core::Run(current_address_space, jit_state, &halt_reason);
is_executing = true;
RequestCacheInvalidation();
return hr;
}
HaltReason Step() {
// HaltReason hr = A32Core::Step(current_address_space, jit_state, &halt_reason);
// RequestCacheInvalidation();
return HaltReason{};
}
void ClearCache() {
std::unique_lock lock{invalidation_mutex};
invalidate_entire_cache = true;
HaltExecution(HaltReason::CacheInvalidation);
}
void InvalidateCacheRange(u32 start_address, size_t length) {
std::unique_lock lock{invalidation_mutex};
invalid_cache_ranges.add(boost::icl::discrete_interval<u32>::closed(start_address, u32(start_address + length - 1)));
HaltExecution(HaltReason::CacheInvalidation);
}
void Reset() {
jit_state = {};
}
void HaltExecution(HaltReason hr) {
Atomic::Or(&halt_reason, ~u32(hr));
}
void ClearHalt(HaltReason hr) {
Atomic::And(&halt_reason, ~u32(hr));
}
std::array<u32, 16>& Regs() {
return jit_state.regs;
}
const std::array<u32, 16>& Regs() const {
return jit_state.regs;
}
std::array<u32, 64>& ExtRegs() {
return jit_state.ext_regs;
}
const std::array<u32, 64>& ExtRegs() const {
return jit_state.ext_regs;
}
u32 Cpsr() const {
return jit_state.cpsr_nzcv;
}
void SetCpsr(u32 value) {
jit_state.cpsr_nzcv = value;
}
u32 Fpscr() const {
return jit_state.fpscr;
}
void SetFpscr(u32 value) {
jit_state.fpscr = value;
}
void ClearExclusiveState() {
jit_state.exclusive_state = false;
}
private:
void RequestCacheInvalidation() {
// UNREACHABLE();
invalidate_entire_cache = false;
invalid_cache_ranges.clear();
}
A32::UserConfig conf;
A32JitState jit_state{};
A32AddressSpace current_address_space;
Jit* jit_interface;
volatile u32 halt_reason = 0;
bool is_executing = false;
boost::icl::interval_set<u32> invalid_cache_ranges;
bool invalidate_entire_cache = false;
std::mutex invalidation_mutex;
};
Jit::Jit(UserConfig conf) : impl(std::make_unique<Impl>(this, conf)) {}
Jit::~Jit() = default;
HaltReason Jit::Run() {
return impl->Run();
}
HaltReason Jit::Step() {
return impl->Step();
}
void Jit::ClearCache() {
impl->ClearCache();
}
void Jit::InvalidateCacheRange(u32 start_address, std::size_t length) {
impl->InvalidateCacheRange(start_address, length);
}
void Jit::Reset() {
impl->Reset();
}
void Jit::HaltExecution(HaltReason hr) {
impl->HaltExecution(hr);
}
void Jit::ClearHalt(HaltReason hr) {
impl->ClearHalt(hr);
}
std::array<u32, 16>& Jit::Regs() {
return impl->Regs();
}
const std::array<u32, 16>& Jit::Regs() const {
return impl->Regs();
}
std::array<u32, 64>& Jit::ExtRegs() {
return impl->ExtRegs();
}
const std::array<u32, 64>& Jit::ExtRegs() const {
return impl->ExtRegs();
}
u32 Jit::Cpsr() const {
return impl->Cpsr();
}
void Jit::SetCpsr(u32 value) {
impl->SetCpsr(value);
}
u32 Jit::Fpscr() const {
return impl->Fpscr();
}
void Jit::SetFpscr(u32 value) {
impl->SetFpscr(value);
}
void Jit::ClearExclusiveState() {
impl->ClearExclusiveState();
}
} // namespace Dynarmic::A32
@@ -0,0 +1,40 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <powah_emit.hpp>
#include <ankerl/unordered_dense.h>
#include "dynarmic/backend/ppc64/code_block.h"
#include "dynarmic/backend/ppc64/emit_ppc64.h"
#include "dynarmic/frontend/A64/a64_location_descriptor.h"
#include "dynarmic/interface/A64/a64.h"
#include "dynarmic/interface/A64/config.h"
#include "dynarmic/ir/terminal.h"
#include "dynarmic/ir/basic_block.h"
namespace Dynarmic::Backend::PPC64 {
struct A64JitState {
using ProgramCounterType = u32;
std::array<u64, 31> regs{};
u64 pc = 0;
alignas(16) std::array<u64, 64> vec{};
u64 sp = 0;
u32 upper_location_descriptor = 0;
u32 exclusive_state = 0;
u32 pstate = 0;
u32 fpcr = 0;
u32 fpsr = 0;
volatile u32 halt_reason = 0;
u32 check_bit = 0;
IR::LocationDescriptor GetLocationDescriptor() const {
const u64 fpcr_u64 = u64(fpcr & A64::LocationDescriptor::fpcr_mask) << A64::LocationDescriptor::fpcr_shift;
const u64 pc_u64 = pc & A64::LocationDescriptor::pc_mask;
return IR::LocationDescriptor{pc_u64 | fpcr_u64};
}
};
} // namespace Dynarmic::Backend::RV64
@@ -0,0 +1,366 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include <memory>
#include <mutex>
#include <cstddef>
#include <boost/icl/interval_set.hpp>
#include "common/assert.h"
#include "common/common_types.h"
#include "dynarmic/frontend/A64/a64_location_descriptor.h"
#include "dynarmic/frontend/A64/translate/a64_translate.h"
#include "dynarmic/interface/A64/config.h"
#include "dynarmic/backend/ppc64/a64_core.h"
#include "dynarmic/common/atomic.h"
#include "dynarmic/ir/opt_passes.h"
#include "dynarmic/interface/A64/a64.h"
namespace Dynarmic::A64 {
using namespace Dynarmic::Backend::PPC64;
using CodePtr = std::uint32_t*;
struct A64AddressSpace final {
explicit A64AddressSpace(const A64::UserConfig& conf)
: conf(conf)
, cb(conf.code_cache_size)
, as(cb.ptr<u8*>(), conf.code_cache_size)
{
}
CodePtr GetOrEmit(IR::LocationDescriptor desc) {
if (auto const it = block_entries.find(desc.Value()); it != block_entries.end())
return it->second;
auto const get_code = [this](u64 vaddr) {
return conf.callbacks->MemoryReadCode(vaddr);
};
ir_block.Reset(A64::LocationDescriptor{desc});
A64::Translate(ir_block, A64::LocationDescriptor{desc}, get_code, {conf.define_unpredictable_behaviour, conf.wall_clock_cntpct});
Optimization::Optimize(ir_block, conf, {});
fmt::print("IR:\n{}\n", IR::DumpBlock(ir_block));
const EmittedBlockInfo block_info = Emit(std::move(ir_block));
block_infos.insert_or_assign(desc.Value(), block_info);
block_entries.insert_or_assign(desc.Value(), block_info.entry_point);
return block_info.entry_point;
}
void ClearCache() {
block_entries.clear();
block_infos.clear();
}
EmittedBlockInfo Emit(IR::Block block) {
EmittedBlockInfo block_info = EmitPPC64(as, std::move(block), {
.enable_cycle_counting = conf.enable_cycle_counting,
.always_little_endian = true,
.a64_variant = true
});
Link(block_info);
return block_info;
}
void Link(EmittedBlockInfo& block_info) {
// TODO(lizzie): Block linking
// UNREACHABLE();
}
IR::Block ir_block = {LocationDescriptor(0, FP::FPCR(0), false)};
const A64::UserConfig conf;
CodeBlock cb;
powah::Context as;
ankerl::unordered_dense::map<u64, CodePtr> block_entries;
ankerl::unordered_dense::map<u64, EmittedBlockInfo> block_infos;
};
struct A64Core final {
using CodeFn = HaltReason (*)(A64AddressSpace*, A64JitState*, volatile u32*, void *fn);
static HaltReason Run(A64AddressSpace& process, A64JitState& thread_ctx, volatile u32* halt_reason) {
HaltReason hr{};
do {
const auto loc = thread_ctx.GetLocationDescriptor();
const auto entry = process.GetOrEmit(loc);
hr = (CodeFn(entry))(&process, &thread_ctx, halt_reason, (void*)&A64Core::Run);
} while (hr == HaltReason(0));
return hr;
}
};
struct Jit::Impl final {
Impl(Jit* jit_interface, A64::UserConfig conf)
: conf(conf)
, current_address_space(conf)
, jit_interface(jit_interface) {}
HaltReason Run() {
ASSERT(!is_executing);
is_executing = true;
HaltReason hr = A64Core::Run(current_address_space, jit_state, &halt_reason);
is_executing = false;
RequestCacheInvalidation();
return hr;
}
HaltReason Step() {
// HaltReason hr = A64Core::Step(current_address_space, jit_state, &halt_reason);
// RequestCacheInvalidation();
return HaltReason{};
}
void ClearCache() {
std::unique_lock lock{invalidation_mutex};
invalidate_entire_cache = true;
HaltExecution(HaltReason::CacheInvalidation);
}
void InvalidateCacheRange(u64 start_address, size_t length) {
std::unique_lock lock{invalidation_mutex};
const auto end_address = u64(start_address + length - 1);
invalid_cache_ranges.add(boost::icl::discrete_interval<u64>::closed(start_address, end_address));
HaltExecution(HaltReason::CacheInvalidation);
}
void Reset() {
ASSERT(!is_executing);
jit_state = {};
}
void HaltExecution(HaltReason hr) {
Atomic::Or(&jit_state.halt_reason, u32(hr));
}
void ClearHalt(HaltReason hr) {
Atomic::And(&jit_state.halt_reason, ~u32(hr));
}
u64 GetSP() const {
return jit_state.sp;
}
void SetSP(u64 value) {
jit_state.sp = value;
}
u64 GetPC() const {
return jit_state.pc;
}
void SetPC(u64 value) {
jit_state.pc = value;
}
u64 GetRegister(size_t index) const {
return index == 31 ? GetSP() : jit_state.regs.at(index);
}
void SetRegister(size_t index, u64 value) {
if (index == 31)
return SetSP(value);
jit_state.regs.at(index) = value;
}
std::array<u64, 31> GetRegisters() const {
return jit_state.regs;
}
void SetRegisters(const std::array<u64, 31>& value) {
jit_state.regs = value;
}
Vector GetVector(size_t index) const {
return {jit_state.vec.at(index * 2), jit_state.vec.at(index * 2 + 1)};
}
void SetVector(size_t index, Vector value) {
jit_state.vec.at(index * 2) = value[0];
jit_state.vec.at(index * 2 + 1) = value[1];
}
std::array<Vector, 32> GetVectors() const {
std::array<Vector, 32> ret;
static_assert(sizeof(ret) == sizeof(jit_state.vec));
std::memcpy(ret.data(), jit_state.vec.data(), sizeof(jit_state.vec));
return ret;
}
void SetVectors(const std::array<Vector, 32>& value) {
static_assert(sizeof(value) == sizeof(jit_state.vec));
std::memcpy(jit_state.vec.data(), value.data(), sizeof(jit_state.vec));
}
u32 GetFpcr() const {
return jit_state.fpcr;
}
void SetFpcr(u32 value) {
jit_state.fpcr = value;
}
u32 GetFpsr() const {
return jit_state.fpsr;
}
void SetFpsr(u32 value) {
jit_state.fpsr = value;
}
u32 GetPstate() const {
return jit_state.pstate;
}
void SetPstate(u32 value) {
jit_state.pstate = value;
}
void ClearExclusiveState() {
jit_state.exclusive_state = 0;
}
bool IsExecuting() const {
return is_executing;
}
std::string Disassemble() const {
// const size_t size = reinterpret_cast<const char*>(block_of_code.getCurr()) - reinterpret_cast<const char*>(block_of_code.GetCodeBegin());
// auto const* p = reinterpret_cast<const char*>(block_of_code.GetCodeBegin());
// return Common::DisassemblePPC64(p, p + size);
return {};
}
private:
void RequestCacheInvalidation() {
// UNREACHABLE();
invalidate_entire_cache = false;
invalid_cache_ranges.clear();
}
A64::UserConfig conf;
A64JitState jit_state{};
A64AddressSpace current_address_space;
Jit* jit_interface;
volatile u32 halt_reason = 0;
bool is_executing = false;
boost::icl::interval_set<u64> invalid_cache_ranges;
bool invalidate_entire_cache = false;
std::mutex invalidation_mutex;
};
Jit::Jit(UserConfig conf) : impl(std::make_unique<Jit::Impl>(this, conf)) {}
Jit::~Jit() = default;
HaltReason Jit::Run() {
return impl->Run();
}
HaltReason Jit::Step() {
return impl->Step();
}
void Jit::ClearCache() {
impl->ClearCache();
}
void Jit::InvalidateCacheRange(u64 start_address, size_t length) {
impl->InvalidateCacheRange(start_address, length);
}
void Jit::Reset() {
impl->Reset();
}
void Jit::HaltExecution(HaltReason hr) {
impl->HaltExecution(hr);
}
void Jit::ClearHalt(HaltReason hr) {
impl->ClearHalt(hr);
}
u64 Jit::GetSP() const {
return impl->GetSP();
}
void Jit::SetSP(u64 value) {
impl->SetSP(value);
}
u64 Jit::GetPC() const {
return impl->GetPC();
}
void Jit::SetPC(u64 value) {
impl->SetPC(value);
}
u64 Jit::GetRegister(size_t index) const {
return impl->GetRegister(index);
}
void Jit::SetRegister(size_t index, u64 value) {
impl->SetRegister(index, value);
}
std::array<u64, 31> Jit::GetRegisters() const {
return impl->GetRegisters();
}
void Jit::SetRegisters(const std::array<u64, 31>& value) {
impl->SetRegisters(value);
}
Vector Jit::GetVector(size_t index) const {
return impl->GetVector(index);
}
void Jit::SetVector(size_t index, Vector value) {
impl->SetVector(index, value);
}
std::array<Vector, 32> Jit::GetVectors() const {
return impl->GetVectors();
}
void Jit::SetVectors(const std::array<Vector, 32>& value) {
impl->SetVectors(value);
}
u32 Jit::GetFpcr() const {
return impl->GetFpcr();
}
void Jit::SetFpcr(u32 value) {
impl->SetFpcr(value);
}
u32 Jit::GetFpsr() const {
return impl->GetFpsr();
}
void Jit::SetFpsr(u32 value) {
impl->SetFpsr(value);
}
u32 Jit::GetPstate() const {
return impl->GetPstate();
}
void Jit::SetPstate(u32 value) {
impl->SetPstate(value);
}
void Jit::ClearExclusiveState() {
impl->ClearExclusiveState();
}
bool Jit::IsExecuting() const {
return impl->IsExecuting();
}
std::string Jit::Disassemble() const {
return impl->Disassemble();
}
} // namespace Dynarmic::A64
@@ -0,0 +1,77 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <powah_emit.hpp>
#include "common/common_types.h"
namespace Dynarmic::Backend::PPC64 {
/*
https://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi.html#REG
r0 Volatile register used in function prologs
r1 Stack frame pointer
r2 TOC pointer
r3 Volatile parameter and return value register
r4-r10 Volatile registers used for function parameters
r11 Volatile register used in calls by pointer and as an environment pointer for languages which require one
r12 Volatile register used for exception handling and glink code
r13 Reserved for use as system thread ID
r14-r31 Nonvolatile registers used for local variables
f0 Volatile scratch register
f1-f4 Volatile floating point parameter and return value registers
f5-f13 Volatile floating point parameter registers
f14-f31 Nonvolatile registers
LR Link register (volatile)
CTR Loop counter register (volatile)
XER Fixed point exception register (volatile)
FPSCR Floating point status and control register (volatile)
CR0-CR1 Volatile condition code register fields
CR2-CR4 Nonvolatile condition code register fields
CR5-CR7 Volatile condition code register fields
v0-v1 Volatile scratch registers
v2-v13 Volatile vector parameters registers
v14-v19 Volatile scratch registers
v20-v31 Non-volatile registers
vrsave Non-volatile 32-bit register
*/
// Jit fn signature => (AXXAddressSpace& process, AXXJitState& thread_ctx, volatile u32* halt_reason)
constexpr powah::GPR RPROCESS = powah::R3;
constexpr powah::GPR RJIT = powah::R4;
constexpr powah::GPR RHALTREASON = powah::R5;
constexpr powah::GPR RTOCPTR = powah::R6;
// temporals
constexpr powah::GPR RNZCV = powah::R7;
constexpr powah::GPR ABI_PARAM1 = powah::R3;
constexpr powah::GPR ABI_PARAM2 = powah::R4;
constexpr powah::GPR ABI_PARAM3 = powah::R5;
constexpr powah::GPR ABI_PARAM4 = powah::R6;
// See https://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi.html#REG
constexpr std::initializer_list<u32> GPR_ORDER{
// r13 is thread-id
14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 //non-volatile
};
constexpr std::initializer_list<powah::GPR> ABI_CALLEER_SAVED{
powah::R3, powah::R4, powah::R5, powah::R6,
powah::R7, powah::R8, powah::R9, powah::R10,
powah::R11, powah::R12
};
constexpr std::initializer_list<powah::GPR> ABI_CALLEE_SAVED{
powah::R14, powah::R15, powah::R16, powah::R17,
powah::R18, powah::R19, powah::R20, powah::R21,
powah::R22, powah::R23, powah::R24, powah::R25,
powah::R26, powah::R27, powah::R28, powah::R29,
powah::R30, powah::R31
};
} // namespace Dynarmic::Backend::RV64
@@ -0,0 +1,37 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <cstdint>
#include <cstdio>
#include <new>
#include <sys/mman.h>
#include "common/common_types.h"
#include "common/assert.h"
namespace Dynarmic::Backend::PPC64 {
class CodeBlock {
public:
explicit CodeBlock(size_t size_) noexcept : size{size_} {
block = (u8*)mmap(nullptr, size, PROT_WRITE | PROT_READ | PROT_EXEC, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
//printf("block = %p, size= %zx\n", block, size);
ASSERT(block != nullptr);
}
~CodeBlock() noexcept {
if (block != nullptr)
munmap(block, size);
}
template<typename T>
T ptr() const noexcept {
static_assert(std::is_pointer_v<T> || std::is_same_v<T, uintptr_t> || std::is_same_v<T, intptr_t>);
return reinterpret_cast<T>(block);
}
protected:
void* block = nullptr;
size_t size = 0;
};
} // namespace Dynarmic::Backend::RV64
@@ -0,0 +1,27 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <powah_emit.hpp>
#include "dynarmic/backend/ppc64/emit_ppc64.h"
#include "dynarmic/backend/ppc64/reg_alloc.h"
namespace Dynarmic::IR {
class Block;
} // namespace Dynarmic::IR
namespace Dynarmic::Backend::PPC64 {
struct EmitConfig;
struct EmitContext {
IR::Block& block;
RegAlloc& reg_alloc;
const EmitConfig& emit_conf;
EmittedBlockInfo& ebi;
// label used when returning :)
powah::Label l_return;
};
} // namespace Dynarmic::Backend::RV64
@@ -0,0 +1,305 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include <bit>
#include <cstdint>
#include <powah_emit.hpp>
#include <fmt/ostream.h>
#include "dynarmic/backend/ppc64/emit_ppc64.h"
#include "dynarmic/backend/ppc64/a32_core.h"
#include "dynarmic/backend/ppc64/a64_core.h"
#include "dynarmic/backend/ppc64/abi.h"
#include "dynarmic/backend/ppc64/a32_core.h"
#include "dynarmic/backend/ppc64/a64_core.h"
#include "dynarmic/backend/ppc64/abi.h"
#include "dynarmic/backend/ppc64/emit_context.h"
#include "dynarmic/backend/ppc64/reg_alloc.h"
#include "dynarmic/backend/ppc64/stack_layout.h"
#include "dynarmic/ir/basic_block.h"
#include "dynarmic/ir/microinstruction.h"
#include "dynarmic/ir/opcodes.h"
#include "stack_layout.h"
namespace Dynarmic::Backend::PPC64 {
template<>
void EmitIR<IR::Opcode::Void>(powah::Context&, EmitContext&, IR::Inst*) {}
template<>
void EmitIR<IR::Opcode::Identity>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const result = ctx.reg_alloc.ScratchGpr();
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
code.MR(result, source);
ctx.reg_alloc.DefineValue(inst, result);
}
template<>
void EmitIR<IR::Opcode::Breakpoint>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::CallHostFunction>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::PushRSB>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::GetCarryFromOp>(powah::Context&, EmitContext& ctx, IR::Inst* inst) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::GetOverflowFromOp>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::GetGEFromOp>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::GetNZCVFromOp>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
if (ctx.reg_alloc.IsValueLive(inst)) {
ASSERT(false && "unimp value live");
return;
}
// All logical operations whom set (RC) are going to compute as the following:
// 1. Rt <- logical_op (Ra, Rb)
// 2. Compare as Signed(Rt) against 0
// Basically, it's equivalent to say:
//
// add r0, r1, r2 -> add. r0, r1, r2
// cmpli r0, 0
//
// CR0:
// 0 - 0x08 - N/LT, result is negative
// 1 - 0x04 - P/GT, result is positive
// 2 - 0x02 - Z/EQ, result is zero
// 3 - 0x01 - S/SO, summary overflow (carry?)
// XER:
// 32 - SO
// 33 - Overflow
// 34 - Carry
// NZCV -> (CR0.0, CR0.2, XER.34, XER.33)
if (false) {
//code.MFSPR(powah::GPR{1}, tmp4, powah::R0); // From XER
/*uint64_t nzcv(uint64_t cr0, uint64_t xer) {
return ((xer >> 33) & 1)
| (((xer >> 34) & 1) << 1)
| (((cr0 >> (32 + 2)) & 1) << 2)
| (((cr0 >> (32 + 0)) & 1) << 3);
}*/
// auto const tmp9 = ctx.reg_alloc.ScratchGpr();
// auto const tmp10 = ctx.reg_alloc.ScratchGpr();
// code.SRDI(tmp9, tmp3, 34);
// code.RLDICL(tmp10, tmp4, 31, 63);
// code.SRDI(tmp3, tmp3, 32);
// code.RLDIC(tmp9, tmp9, 2, 61);
// code.OR(tmp9, tmp9, tmp10);
// code.RLDIC(tmp3, tmp3, 3, 60);
// code.SRDI(tmp4, tmp4, 34);
// code.OR(tmp3, tmp9, tmp3);
// code.RLDIC(tmp4, tmp4, 1, 62);
// code.OR(tmp3, tmp3, tmp4);
} else {
// MFCR Fills RT 32:63, RT 0:31 left blank
auto const tmp3 = ctx.reg_alloc.ScratchGpr();
code.MR(tmp3, PPC64::RNZCV);
ctx.reg_alloc.DefineValue(inst, tmp3);
}
}
template<>
void EmitIR<IR::Opcode::GetNZFromOp>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::GetUpperFromOp>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::GetLowerFromOp>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::GetCFlagFromNZCV>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::NZCVFromPackedFlags>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
namespace {
void EmitLeafTerminal(powah::Context& code, EmitContext& ctx, IR::Term::LeafTerminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step);
void EmitTerminal(powah::Context& code, EmitContext& ctx, IR::Term::Terminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step);
void EmitTerminal(powah::Context& code, EmitContext& ctx, IR::Term::ReturnToDispatch, IR::LocationDescriptor, bool) {
ASSERT(false && "unimp");
}
// r3 -> process
// r4 -> thread_ctx
// r5 -> halt_reason
// r6 -> test_thunk
void EmitTerminal(powah::Context& code, EmitContext& ctx, IR::Term::LinkBlock terminal, IR::LocationDescriptor initial_location, bool) {
if (ctx.emit_conf.a64_variant) {
auto const tmp = ctx.reg_alloc.ScratchGpr();
code.LI(tmp, terminal.next.Value());
code.STD(tmp, PPC64::RJIT, offsetof(A64JitState, pc));
code.B(ctx.l_return);
} else {
auto const tmp = ctx.reg_alloc.ScratchGpr();
code.LI(tmp, terminal.next.Value());
code.STW(tmp, PPC64::RJIT, offsetof(A32JitState, regs) + sizeof(u32) * 15);
ASSERT(false && "unimp");
}
}
void EmitTerminal(powah::Context& code, EmitContext& ctx, IR::Term::LinkBlockFast terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
EmitTerminal(code, ctx, IR::Term::LinkBlock{terminal.next}, initial_location, is_single_step);
}
void EmitTerminal(powah::Context& code, EmitContext& ctx, IR::Term::PopRSBHint, IR::LocationDescriptor, bool) {
ASSERT(false && "unimp");
}
void EmitTerminal(powah::Context& code, EmitContext& ctx, IR::Term::FastDispatchHint, IR::LocationDescriptor, bool) {
ASSERT(false && "unimp");
}
void EmitTerminal(powah::Context& code, EmitContext& ctx, IR::Term::If terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
ASSERT(false && "unimp");
}
void EmitTerminal(powah::Context& code, EmitContext& ctx, IR::Term::CheckBit terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
powah::Label const l_else = code.DefineLabel();
powah::Label const l_end = code.DefineLabel();
auto const tmp = ctx.reg_alloc.ScratchGpr();
code.LWZ(tmp, PPC64::RJIT, offsetof(A64JitState, check_bit));
code.CMPLDI(tmp, 0);
code.BEQ(powah::CR0, l_else);
// CheckBit == 1
EmitLeafTerminal(code, ctx, terminal.then_, initial_location, is_single_step);
code.B(l_end);
// CheckBit == 0
code.LABEL(l_else);
EmitLeafTerminal(code, ctx, terminal.else_, initial_location, is_single_step);
code.LABEL(l_end);
}
void EmitTerminal(powah::Context& code, EmitContext& ctx, IR::Term::CheckHalt terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
ASSERT(false && "unimp");
}
void EmitLeafTerminal(powah::Context& code, EmitContext& ctx, IR::Term::LeafTerminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
if (auto const x = std::get_if<IR::Term::ReturnToDispatch>(&terminal))
return EmitTerminal(code, ctx, *x, initial_location, is_single_step);
if (auto const x = std::get_if<IR::Term::LinkBlock>(&terminal))
return EmitTerminal(code, ctx, *x, initial_location, is_single_step);
if (auto const x = std::get_if<IR::Term::LinkBlockFast>(&terminal))
return EmitTerminal(code, ctx, *x, initial_location, is_single_step);
if (auto const x = std::get_if<IR::Term::PopRSBHint>(&terminal))
return EmitTerminal(code, ctx, *x, initial_location, is_single_step);
if (auto const x = std::get_if<IR::Term::FastDispatchHint>(&terminal))
return EmitTerminal(code, ctx, *x, initial_location, is_single_step);
UNREACHABLE();
}
void EmitTerminal(powah::Context& code, EmitContext& ctx, IR::Term::Terminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
if (auto const x = std::get_if<IR::Term::LeafTerminal>(&terminal))
return EmitLeafTerminal(code, ctx, *x, initial_location, is_single_step);
if (auto const x = std::get_if<IR::Term::If>(&terminal))
return EmitTerminal(code, ctx, *x, initial_location, is_single_step);
if (auto const x = std::get_if<IR::Term::CheckBit>(&terminal))
return EmitTerminal(code, ctx, *x, initial_location, is_single_step);
if (auto const x = std::get_if<IR::Term::CheckHalt>(&terminal))
return EmitTerminal(code, ctx, *x, initial_location, is_single_step);
UNREACHABLE();
}
}
EmittedBlockInfo EmitPPC64(powah::Context& code, IR::Block block, const EmitConfig& emit_conf) {
EmittedBlockInfo ebi;
RegAlloc reg_alloc{code};
EmitContext ctx{block, reg_alloc, emit_conf, ebi, code.DefineLabel()};
size_t const stack_size = 112 + ABI_CALLEE_SAVED.size() * 8;
auto const start_offset = code.offset;
ebi.entry_point = &code.base[start_offset];
if (!block.instructions.empty()) {
code.MFLR(powah::R0);
code.STD(powah::R0, powah::R1, 16);
// Non-volatile saves
std::vector<powah::GPR> gp_regs{ABI_CALLEE_SAVED};
for (size_t i = 0; i < gp_regs.size(); ++i)
code.STD(gp_regs[i], powah::R1, -int32_t(gp_regs.size() - i) * 8);
code.STDU(powah::R1, powah::R1, uint32_t(-stack_size));
code.STD(powah::R2, powah::R1, 40);
for (auto iter = block.instructions.begin(); iter != block.instructions.end(); ++iter) {
IR::Inst* inst = &*iter;
switch (inst->GetOpcode()) {
#define OPCODE(name, type, ...) \
case IR::Opcode::name: \
EmitIR<IR::Opcode::name>(code, ctx, inst); \
break;
#define A32OPC(name, type, ...) \
case IR::Opcode::A32##name: \
EmitIR<IR::Opcode::A32##name>(code, ctx, inst); \
break;
#define A64OPC(name, type, ...) \
case IR::Opcode::A64##name: \
EmitIR<IR::Opcode::A64##name>(code, ctx, inst); \
break;
#include "dynarmic/ir/opcodes.inc"
#undef OPCODE
#undef A32OPC
#undef A64OPC
default:
UNREACHABLE();
}
}
// auto const cycles_to_add = block.CycleCount();
EmitTerminal(code, ctx, ctx.block.GetTerminal(), ctx.block.Location(), false);
code.LABEL(ctx.l_return);
code.ADDI(powah::R1, powah::R1, stack_size);
for (size_t i = 0; i < gp_regs.size(); ++i)
code.LD(gp_regs[i], powah::R1, -int32_t(gp_regs.size() - i) * 8);
code.LD(powah::R0, powah::R1, 16);
code.MTLR(powah::R0);
code.LI(powah::R3, 0);
} else {
EmitTerminal(code, ctx, ctx.block.GetTerminal(), ctx.block.Location(), false);
code.LABEL(ctx.l_return);
code.LI(powah::R3, 67); //non-zero ret
}
code.BLR();
code.ApplyRelocs();
/*
llvm-objcopy -I binary -O elf64-powerpc --rename-section=.data=.text,code test.bin test.elf && llvm-objdump -d test.elf
*/
static FILE* fp = fopen("test.bin", "wb");
fwrite(code.base, code.offset - start_offset, sizeof(uint32_t), fp);
fflush(fp);
ebi.size = code.offset - start_offset;
return ebi;
}
} // namespace Dynarmic::Backend::RV64
@@ -0,0 +1,41 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <vector>
#include <powah_emit.hpp>
#include "common/common_types.h"
namespace biscuit {
class Assembler;
} // namespace biscuit
namespace Dynarmic::IR {
class Block;
class Inst;
enum class Cond;
enum class Opcode;
} // namespace Dynarmic::IR
namespace Dynarmic::Backend::PPC64 {
struct EmittedBlockInfo {
std::uint32_t* entry_point;
size_t size;
};
struct EmitConfig {
bool enable_cycle_counting;
bool always_little_endian;
bool a64_variant;
};
struct EmitContext;
EmittedBlockInfo EmitPPC64(powah::Context& code, IR::Block block, const EmitConfig& emit_conf);
template<IR::Opcode op>
void EmitIR(powah::Context& code, EmitContext& ctx, IR::Inst* inst);
} // namespace Dynarmic::Backend::RV64
@@ -0,0 +1,318 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include <powah_emit.hpp>
#include <fmt/ostream.h>
#include "dynarmic/frontend/A32/a32_types.h"
#include "dynarmic/backend/ppc64/a32_core.h"
#include "dynarmic/backend/ppc64/abi.h"
#include "dynarmic/backend/ppc64/emit_context.h"
#include "dynarmic/backend/ppc64/emit_ppc64.h"
#include "dynarmic/backend/ppc64/reg_alloc.h"
#include "dynarmic/ir/basic_block.h"
#include "dynarmic/ir/microinstruction.h"
#include "dynarmic/ir/opcodes.h"
namespace Dynarmic::Backend::PPC64 {
template<>
void EmitIR<IR::Opcode::A32SetCheckBit>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32GetRegister>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
if (inst->GetArg(0).GetType() == IR::Type::A32Reg) {
auto const result = ctx.reg_alloc.ScratchGpr();
code.ADDI(result, PPC64::RJIT, A32::RegNumber(inst->GetArg(0).GetA32RegRef()) * sizeof(u32));
code.LWZ(result, result, offsetof(A32JitState, regs));
ctx.reg_alloc.DefineValue(inst, result);
} else {
ASSERT(false && "unimp");
}
}
template<>
void EmitIR<IR::Opcode::A32GetExtendedRegister32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32GetExtendedRegister64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32GetVector>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32SetRegister>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const value = ctx.reg_alloc.UseGpr(inst->GetArg(1));
if (inst->GetArg(0).GetType() == IR::Type::A32Reg) {
auto const addr = ctx.reg_alloc.ScratchGpr();
code.ADDI(addr, PPC64::RJIT, A32::RegNumber(inst->GetArg(0).GetA32RegRef()) * sizeof(u32));
code.STW(value, addr, offsetof(A32JitState, regs));
} else {
ASSERT(false && "unimp");
}
}
template<>
void EmitIR<IR::Opcode::A32SetExtendedRegister32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32SetExtendedRegister64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32SetVector>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32GetCpsr>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32SetCpsr>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32SetCpsrNZCV>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32SetCpsrNZCVRaw>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32SetCpsrNZCVQ>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32SetCpsrNZ>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32SetCpsrNZC>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32GetCFlag>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const result = ctx.reg_alloc.ScratchGpr();
code.LD(result, PPC64::RJIT, offsetof(A32JitState, cpsr_nzcv));
code.RLWINM(result, result, 31, 31, 31);
ctx.reg_alloc.DefineValue(inst, result);
}
template<>
void EmitIR<IR::Opcode::A32OrQFlag>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32GetGEFlags>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32SetGEFlags>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32SetGEFlagsCompressed>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32BXWritePC>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32UpdateUpperLocationDescriptor>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32CallSupervisor>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32ExceptionRaised>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32DataSynchronizationBarrier>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32DataMemoryBarrier>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32InstructionSynchronizationBarrier>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32GetFpscr>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32SetFpscr>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32GetFpscrNZCV>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32SetFpscrNZCV>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
// Memory
template<>
void EmitIR<IR::Opcode::A32ClearExclusive>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32ReadMemory8>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32ReadMemory16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32ReadMemory32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32ReadMemory64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32ExclusiveReadMemory8>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32ExclusiveReadMemory16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32ExclusiveReadMemory32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32ExclusiveReadMemory64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32WriteMemory8>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32WriteMemory16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32WriteMemory32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32WriteMemory64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32ExclusiveWriteMemory8>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32ExclusiveWriteMemory16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32ExclusiveWriteMemory32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32ExclusiveWriteMemory64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
// Coprocesor
template<>
void EmitIR<IR::Opcode::A32CoprocInternalOperation>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32CoprocSendOneWord>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32CoprocSendTwoWords>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32CoprocGetOneWord>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32CoprocGetTwoWords>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32CoprocLoadWords>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32CoprocStoreWords>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
} // namespace Dynarmic::Backend::RV64
@@ -0,0 +1,341 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include <powah_emit.hpp>
#include <fmt/ostream.h>
#include "dynarmic/frontend/A64/a64_types.h"
#include "dynarmic/backend/ppc64/a64_core.h"
#include "dynarmic/backend/ppc64/abi.h"
#include "dynarmic/backend/ppc64/emit_context.h"
#include "dynarmic/backend/ppc64/emit_ppc64.h"
#include "dynarmic/backend/ppc64/reg_alloc.h"
#include "dynarmic/ir/basic_block.h"
#include "dynarmic/ir/microinstruction.h"
#include "dynarmic/ir/opcodes.h"
namespace Dynarmic::Backend::PPC64 {
template<>
void EmitIR<IR::Opcode::A64SetCheckBit>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const value = ctx.reg_alloc.UseGpr(inst->GetArg(0));
code.STW(value, PPC64::RJIT, offsetof(A64JitState, check_bit));
}
template<>
void EmitIR<IR::Opcode::A64GetCFlag>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A64GetNZCVRaw>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A64SetNZCVRaw>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A64SetNZCV>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const value = ctx.reg_alloc.UseGpr(inst->GetArg(0));
code.STW(value, PPC64::RJIT, offsetof(A64JitState, pstate));
}
template<>
void EmitIR<IR::Opcode::A64GetW>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
if (inst->GetArg(0).GetType() == IR::Type::A64Reg) {
auto const result = ctx.reg_alloc.ScratchGpr();
// Need to account for endianess here...
auto const offs = offsetof(A64JitState, regs)
+ A64::RegNumber(inst->GetArg(0).GetA64RegRef()) * sizeof(u64);
code.LWZ(result, PPC64::RJIT, offs);
ctx.reg_alloc.DefineValue(inst, result);
} else {
ASSERT(false && "unimp");
}
}
template<>
void EmitIR<IR::Opcode::A64GetX>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
if (inst->GetArg(0).GetType() == IR::Type::A64Reg) {
auto const result = ctx.reg_alloc.ScratchGpr();
auto const offs = offsetof(A64JitState, regs)
+ A64::RegNumber(inst->GetArg(0).GetA64RegRef()) * sizeof(u64);
code.LD(result, PPC64::RJIT, offs);
ctx.reg_alloc.DefineValue(inst, result);
} else {
ASSERT(false && "unimp");
}
}
template<>
void EmitIR<IR::Opcode::A64GetS>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A64GetD>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A64GetQ>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A64GetSP>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A64GetFPCR>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A64GetFPSR>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A64SetW>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const value = ctx.reg_alloc.UseGpr(inst->GetArg(1));
if (inst->GetArg(0).GetType() == IR::Type::A64Reg) {
auto const tmp = ctx.reg_alloc.ScratchGpr();
auto const offs = offsetof(A64JitState, regs)
+ A64::RegNumber(inst->GetArg(0).GetA64RegRef()) * sizeof(u64);
code.RLDICL(tmp, value, 0, 32);
code.STD(tmp, PPC64::RJIT, offs);
} else {
ASSERT(false && "unimp");
}
}
template<>
void EmitIR<IR::Opcode::A64SetX>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const value = ctx.reg_alloc.UseGpr(inst->GetArg(1));
if (inst->GetArg(0).GetType() == IR::Type::A64Reg) {
auto const offs = offsetof(A64JitState, regs)
+ A64::RegNumber(inst->GetArg(0).GetA64RegRef()) * sizeof(u64);
code.STD(value, PPC64::RJIT, offs);
} else {
ASSERT(false && "unimp");
}
}
template<>
void EmitIR<IR::Opcode::A64SetS>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A64SetD>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A64SetQ>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A64SetSP>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A64SetFPCR>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A64SetFPSR>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A64SetPC>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A64CallSupervisor>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A64ExceptionRaised>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A64DataCacheOperationRaised>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A64InstructionCacheOperationRaised>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A64DataSynchronizationBarrier>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A64DataMemoryBarrier>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A64InstructionSynchronizationBarrier>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A64GetCNTFRQ>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A64GetCNTPCT>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A64GetCTR>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A64GetDCZID>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A64GetTPIDR>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A64GetTPIDRRO>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A64SetTPIDR>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
ASSERT(false && "unimp");
}
// Memory
template<>
void EmitIR<IR::Opcode::A64ClearExclusive>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A64ReadMemory8>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A64ReadMemory16>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A64ReadMemory32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A64ReadMemory64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A64ReadMemory128>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A64ExclusiveReadMemory8>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A64ExclusiveReadMemory16>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A64ExclusiveReadMemory32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A64ExclusiveReadMemory64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A64ExclusiveReadMemory128>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A64WriteMemory8>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A64WriteMemory16>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A64WriteMemory32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A64WriteMemory64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A64WriteMemory128>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A64ExclusiveWriteMemory8>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A64ExclusiveWriteMemory16>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A64ExclusiveWriteMemory32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A64ExclusiveWriteMemory64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A64ExclusiveWriteMemory128>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
ASSERT(false && "unimp");
}
} // namespace Dynarmic::Backend::RV64
@@ -0,0 +1,933 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include <powah_emit.hpp>
#include <fmt/ostream.h>
#include "common/assert.h"
#include "dynarmic/backend/ppc64/a32_core.h"
#include "dynarmic/backend/ppc64/abi.h"
#include "dynarmic/backend/ppc64/emit_context.h"
#include "dynarmic/backend/ppc64/emit_ppc64.h"
#include "dynarmic/backend/ppc64/reg_alloc.h"
#include "dynarmic/ir/basic_block.h"
#include "dynarmic/ir/microinstruction.h"
#include "dynarmic/ir/opcodes.h"
namespace Dynarmic::Backend::PPC64 {
// uint64_t pack2x1(uint32_t lo, uint32_t hi) { return (uint64_t)lo | ((uint64_t)hi << 32); }
template<>
void EmitIR<IR::Opcode::Pack2x32To1x64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const lo = ctx.reg_alloc.UseGpr(inst->GetArg(0));
auto const hi = ctx.reg_alloc.UseGpr(inst->GetArg(1));
auto const result = ctx.reg_alloc.ScratchGpr();
code.SLDI(result, hi, 32);
code.OR(result, result, lo);
ctx.reg_alloc.DefineValue(inst, result);
}
template<>
void EmitIR<IR::Opcode::Pack2x64To1x128>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
ASSERT(false && "unimp");
}
// uint64_t lsw(uint64_t a) { return (uint32_t)a; }
template<>
void EmitIR<IR::Opcode::LeastSignificantWord>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const result = ctx.reg_alloc.ScratchGpr();
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
code.RLDICL(result, source, 0, 32);
ctx.reg_alloc.DefineValue(inst, result);
}
// uint64_t f(uint64_t a) { return (uint16_t)a; }
template<>
void EmitIR<IR::Opcode::LeastSignificantHalf>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const result = ctx.reg_alloc.ScratchGpr();
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
code.RLWINM(result, source, 0, 0xffff);
ctx.reg_alloc.DefineValue(inst, result);
}
// uint64_t f(uint64_t a) { return (uint8_t)a; }
template<>
void EmitIR<IR::Opcode::LeastSignificantByte>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const result = ctx.reg_alloc.ScratchGpr();
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
code.RLWINM(result, source, 0, 0xff);
ctx.reg_alloc.DefineValue(inst, result);
}
// uint64_t msw(uint64_t a) { return a >> 32; }
template<>
void EmitIR<IR::Opcode::MostSignificantWord>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const result = ctx.reg_alloc.ScratchGpr();
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
code.SRDI(result, source, 32);
ctx.reg_alloc.DefineValue(inst, result);
}
/*
uint64_t f(uint64_t a) { return ((uint32_t)a) >> 31; }
*/
template<>
void EmitIR<IR::Opcode::MostSignificantBit>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const result = ctx.reg_alloc.ScratchGpr();
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
code.RLWINM(result, source, 1, 31, 31);
ctx.reg_alloc.DefineValue(inst, result);
}
/*
uint64_t f(uint64_t a) { return (uint32_t)a == 0; }
*/
template<>
void EmitIR<IR::Opcode::IsZero32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const result = ctx.reg_alloc.ScratchGpr();
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
code.CNTLZW(result, source);
code.SRWI(result, result, 5);
ctx.reg_alloc.DefineValue(inst, result);
}
/*
uint64_t f(uint64_t a) { return a == 0; }
*/
template<>
void EmitIR<IR::Opcode::IsZero64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const result = ctx.reg_alloc.ScratchGpr();
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
code.CNTLZD(result, source);
code.SRDI(result, result, 6);
ctx.reg_alloc.DefineValue(inst, result);
}
/*
uint64_t f(uint64_t a) { return (a & 1) != 0; }
*/
template<>
void EmitIR<IR::Opcode::TestBit>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const result = ctx.reg_alloc.ScratchGpr();
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
if (inst->GetArg(1).IsImmediate()) {
auto const shift = inst->GetArg(1).GetImmediateAsU64();
if (shift > 0) {
code.RLDICL(result, source, (64 - shift - 1) & 0x3f, 63);
}
} else {
ASSERT(false && "unimp");
}
ctx.reg_alloc.DefineValue(inst, result);
}
/*
struct jit {
uint64_t t;
uint64_t a;
};
uint64_t f(jit *p, uint64_t a, uint64_t b) {
bool n = (p->a & 0b1000) != 0;
bool z = (p->a & 0b0100) != 0;
bool c = (p->a & 0b0010) != 0;
bool v = (p->a & 0b0001) != 0;
return (p->a & 0b0001) == 0 ? a : b;
}
*/
static powah::GPR EmitConditionalSelectX(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const nzcv = ctx.reg_alloc.ScratchGpr();
auto const then_ = ctx.reg_alloc.UseGpr(inst->GetArg(1));
auto const else_ = ctx.reg_alloc.UseGpr(inst->GetArg(2));
switch (inst->GetArg(0).GetCond()) {
case IR::Cond::EQ: // Z == 1
code.LD(nzcv, PPC64::RJIT, offsetof(A32JitState, cpsr_nzcv));
code.ANDI_(nzcv, nzcv, 4);
code.ISELGT(nzcv, then_, else_);
break;
case IR::Cond::NE: // Z == 0
code.LD(nzcv, PPC64::RJIT, offsetof(A32JitState, cpsr_nzcv));
code.ANDI_(nzcv, nzcv, 4);
code.ISELGT(nzcv, then_, else_);
break;
case IR::Cond::CS: // C == 1
code.LD(nzcv, PPC64::RJIT, offsetof(A32JitState, cpsr_nzcv));
code.ANDI_(nzcv, nzcv, 2);
code.ISELGT(nzcv, then_, else_);
break;
case IR::Cond::CC: // C == 0
code.LD(nzcv, PPC64::RJIT, offsetof(A32JitState, cpsr_nzcv));
code.ANDI_(nzcv, nzcv, 2);
code.ISELGT(nzcv, then_, else_);
break;
case IR::Cond::MI: // N == 1
code.LD(nzcv, PPC64::RJIT, offsetof(A32JitState, cpsr_nzcv));
code.ANDI_(nzcv, nzcv, 8);
code.ISELGT(nzcv, then_, else_);
break;
case IR::Cond::PL: // N == 0
code.LD(nzcv, PPC64::RJIT, offsetof(A32JitState, cpsr_nzcv));
code.ANDI_(nzcv, nzcv, 8);
code.ISELGT(nzcv, then_, else_);
break;
case IR::Cond::VS: // V == 1
code.LD(nzcv, PPC64::RJIT, offsetof(A32JitState, cpsr_nzcv));
code.ANDI_(nzcv, nzcv, 1);
code.ISELGT(nzcv, then_, else_);
break;
case IR::Cond::VC: // V == 0
code.LD(nzcv, PPC64::RJIT, offsetof(A32JitState, cpsr_nzcv));
code.ANDI_(nzcv, nzcv, 1);
code.ISELGT(nzcv, else_, then_);
break;
case IR::Cond::HI: // Z == 0 && C == 1
code.LD(nzcv, PPC64::RJIT, offsetof(A32JitState, cpsr_nzcv));
code.RLWINM(nzcv, nzcv, 0, 29, 30);
code.CMPLDI(nzcv, 2);
code.ISELEQ(nzcv, then_, else_);
break;
case IR::Cond::LS: // Z == 1 || C == 0
code.LD(nzcv, PPC64::RJIT, offsetof(A32JitState, cpsr_nzcv));
code.RLWINM(nzcv, nzcv, 0, 29, 30);
code.CMPLDI(nzcv, 2);
code.ISELEQ(nzcv, else_, then_);
break;
case IR::Cond::GE: { // N == V
auto const tmp = ctx.reg_alloc.ScratchGpr();
code.LWZ(nzcv, PPC64::RJIT, offsetof(A32JitState, cpsr_nzcv));
code.SRWI(tmp, nzcv, 3);
code.XOR(nzcv, tmp, nzcv);
code.ANDI_(nzcv, nzcv, 1);
code.ISELGT(nzcv, else_, then_);
} break;
case IR::Cond::LT: { // N != V
auto const tmp = ctx.reg_alloc.ScratchGpr();
code.LWZ(nzcv, PPC64::RJIT, offsetof(A32JitState, cpsr_nzcv));
code.SRWI(tmp, nzcv, 3);
code.XOR(nzcv, tmp, nzcv);
code.ANDI_(nzcv, nzcv, 1);
code.ISELGT(nzcv, then_, else_);
} break;
case IR::Cond::GT: { // Z == 0 && N == V
auto const tmp = ctx.reg_alloc.ScratchGpr();
powah::Label const l_ne = code.DefineLabel();
powah::Label const l_cc = code.DefineLabel();
powah::Label const l_fi = code.DefineLabel();
code.LD(nzcv, PPC64::RJIT, offsetof(A32JitState, cpsr_nzcv));
code.ANDI_(tmp, nzcv, 4);
code.BNE(powah::CR0, l_ne);
code.SRWI(tmp, nzcv, 3);
code.XOR(nzcv, tmp, nzcv);
code.ANDI_(nzcv, nzcv, 1);
code.CMPLDI(nzcv, 0);
code.BGT(powah::CR0, l_fi);
code.LABEL(l_ne);
code.MR(then_, else_);
code.LABEL(l_cc);
code.MR(nzcv, then_);
code.LABEL(l_fi);
} break;
case IR::Cond::LE: { // Z == 1 || N != V
auto const tmp = ctx.reg_alloc.ScratchGpr();
auto const tmp2 = ctx.reg_alloc.ScratchGpr();
powah::Label const l_ne = code.DefineLabel();
code.MR(tmp2, then_);
code.LD(nzcv, PPC64::RJIT, offsetof(A32JitState, cpsr_nzcv));
code.ANDI_(tmp, nzcv, 4);
code.BNE(powah::CR0, l_ne);
code.SRWI(tmp, nzcv, 3);
code.XOR(nzcv, tmp, nzcv);
code.ANDI_(nzcv, nzcv, 1);
code.ISELGT(tmp2, then_, else_);
code.LABEL(l_ne);
code.MR(nzcv, tmp2);
} break;
default:
ASSERT(false && "unimp");
}
return nzcv;
}
template<>
void EmitIR<IR::Opcode::ConditionalSelect32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const result = EmitConditionalSelectX(code, ctx, inst);
code.RLDICL(result, result, 0, 32);
ctx.reg_alloc.DefineValue(inst, result);
}
template<>
void EmitIR<IR::Opcode::ConditionalSelect64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const result = EmitConditionalSelectX(code, ctx, inst);
ctx.reg_alloc.DefineValue(inst, result);
}
template<>
void EmitIR<IR::Opcode::ConditionalSelectNZCV>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const result = EmitConditionalSelectX(code, ctx, inst);
code.RLDICL(result, result, 0, 32);
ctx.reg_alloc.DefineValue(inst, result);
}
template<>
void EmitIR<IR::Opcode::LogicalShiftLeft32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const result = ctx.reg_alloc.ScratchGpr();
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
auto const shift = ctx.reg_alloc.UseGpr(inst->GetArg(1));
code.SLW(result, source, shift);
code.RLDICL(result, result, 0, 32);
ctx.reg_alloc.DefineValue(inst, result);
}
template<>
void EmitIR<IR::Opcode::LogicalShiftLeft64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const result = ctx.reg_alloc.ScratchGpr();
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
auto const shift = ctx.reg_alloc.UseGpr(inst->GetArg(1));
code.SLD(result, source, shift);
ctx.reg_alloc.DefineValue(inst, result);
}
template<>
void EmitIR<IR::Opcode::LogicalShiftRight32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const result = ctx.reg_alloc.ScratchGpr();
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
auto const shift = ctx.reg_alloc.UseGpr(inst->GetArg(1));
code.SRW(result, source, shift);
code.RLDICL(result, result, 0, 32);
ctx.reg_alloc.DefineValue(inst, result);
}
/*
uint64_t f(uint64_t a, uint64_t s) { return a >> s; }
*/
template<>
void EmitIR<IR::Opcode::LogicalShiftRight64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const result = ctx.reg_alloc.ScratchGpr();
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
auto const shift = ctx.reg_alloc.UseGpr(inst->GetArg(1));
code.SRD(result, source, shift);
ctx.reg_alloc.DefineValue(inst, result);
}
template<>
void EmitIR<IR::Opcode::ArithmeticShiftRight32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const result = ctx.reg_alloc.ScratchGpr();
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
auto const shift = ctx.reg_alloc.UseGpr(inst->GetArg(1));
code.SRAW(result, source, shift);
code.RLDICL(result, result, 0, 32);
ctx.reg_alloc.DefineValue(inst, result);
}
template<>
void EmitIR<IR::Opcode::ArithmeticShiftRight64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const result = ctx.reg_alloc.ScratchGpr();
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
auto const shift = ctx.reg_alloc.UseGpr(inst->GetArg(1));
code.SRAD(result, source, shift);
ctx.reg_alloc.DefineValue(inst, result);
}
// __builtin_rotateright32
template<>
void EmitIR<IR::Opcode::BitRotateRight32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const result = ctx.reg_alloc.ScratchGpr();
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(0));
code.NEG(result, src_a, powah::R0);
code.ROTLW(result, result, src_b);
code.RLDICL(result, result, 0, 32);
ctx.reg_alloc.DefineValue(inst, result);
}
template<>
void EmitIR<IR::Opcode::BitRotateRight64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const result = ctx.reg_alloc.ScratchGpr();
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(0));
code.NEG(result, src_a, powah::R0);
code.ROTLD(result, result, src_b);
ctx.reg_alloc.DefineValue(inst, result);
}
template<>
void EmitIR<IR::Opcode::RotateRightExtended>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const result = ctx.reg_alloc.ScratchGpr();
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(0));
code.NEG(result, src_a, powah::R0);
code.ROTLD(result, result, src_b);
ctx.reg_alloc.DefineValue(inst, result);
}
template<>
void EmitIR<IR::Opcode::LogicalShiftLeftMasked32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const result = ctx.reg_alloc.ScratchGpr();
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
code.SLW(result, source, source);
code.RLDICL(result, result, 0, 32);
ctx.reg_alloc.DefineValue(inst, result);
}
template<>
void EmitIR<IR::Opcode::LogicalShiftLeftMasked64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const result = ctx.reg_alloc.ScratchGpr();
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
code.SLD(result, source, source);
ctx.reg_alloc.DefineValue(inst, result);
}
template<>
void EmitIR<IR::Opcode::LogicalShiftRightMasked32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const result = ctx.reg_alloc.ScratchGpr();
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
code.SRW(result, source, source);
code.RLDICL(result, result, 0, 32);
ctx.reg_alloc.DefineValue(inst, result);
}
template<>
void EmitIR<IR::Opcode::LogicalShiftRightMasked64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const result = ctx.reg_alloc.ScratchGpr();
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
code.SRD(result, source, source);
ctx.reg_alloc.DefineValue(inst, result);
}
template<>
void EmitIR<IR::Opcode::ArithmeticShiftRightMasked32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const result = ctx.reg_alloc.ScratchGpr();
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
code.SRAW(result, source, source);
code.RLDICL(result, result, 0, 32);
ctx.reg_alloc.DefineValue(inst, result);
}
template<>
void EmitIR<IR::Opcode::ArithmeticShiftRightMasked64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const result = ctx.reg_alloc.ScratchGpr();
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
code.SRAD(result, source, source);
ctx.reg_alloc.DefineValue(inst, result);
}
template<>
void EmitIR<IR::Opcode::RotateRightMasked32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const result = ctx.reg_alloc.ScratchGpr();
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(0));
code.NEG(result, src_a, powah::R0);
code.ROTLD(result, result, src_b);
code.RLDICL(result, result, 0, 32);
ctx.reg_alloc.DefineValue(inst, result);
}
template<>
void EmitIR<IR::Opcode::RotateRightMasked64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const result = ctx.reg_alloc.ScratchGpr();
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(0));
code.NEG(result, src_a, powah::R0);
code.ROTLD(result, result, src_b);
ctx.reg_alloc.DefineValue(inst, result);
}
template<>
void EmitIR<IR::Opcode::Add32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const result = ctx.reg_alloc.ScratchGpr();
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(1));
code.ADDC_(result, src_a, src_b);
code.RLDICL(result, result, 0, 32);
ctx.reg_alloc.DefineValue(inst, result);
}
/*
struct jit {
uint32_t nzcv;
};
uint64_t addc(jit *p, uint64_t a, uint64_t b) {
long long unsigned int e;
uint64_t r = __builtin_addcll(a, b, p->nzcv & 0b0010, &e);
p->nzcv = (p->nzcv & 0b1101) | e;
return r;
}
*/
template<>
void EmitIR<IR::Opcode::Add64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const result = ctx.reg_alloc.ScratchGpr();
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(1));
code.ADDC_(result, src_a, src_b);
ctx.reg_alloc.DefineValue(inst, result);
}
template<>
void EmitIR<IR::Opcode::Sub32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const result = ctx.reg_alloc.ScratchGpr();
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(1));
code.SUBFC_(result, src_b, src_a);
code.RLDICL(result, result, 0, 32);
ctx.reg_alloc.DefineValue(inst, result);
}
template<>
void EmitIR<IR::Opcode::Sub64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const result = ctx.reg_alloc.ScratchGpr();
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(1));
code.SUBFC_(result, src_b, src_a);
ctx.reg_alloc.DefineValue(inst, result);
}
template<>
void EmitIR<IR::Opcode::Mul32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const result = ctx.reg_alloc.ScratchGpr();
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(1));
code.MULLWO_(result, src_a, src_b);
code.RLDICL(result, result, 0, 32);
ctx.reg_alloc.DefineValue(inst, result);
}
template<>
void EmitIR<IR::Opcode::Mul64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const result = ctx.reg_alloc.ScratchGpr();
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(1));
code.MULLDO_(result, src_a, src_b);
ctx.reg_alloc.DefineValue(inst, result);
}
template<>
void EmitIR<IR::Opcode::SignedMultiplyHigh64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const result = ctx.reg_alloc.ScratchGpr();
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(1));
code.MULLDO_(result, src_a, src_b);
ctx.reg_alloc.DefineValue(inst, result);
}
template<>
void EmitIR<IR::Opcode::UnsignedMultiplyHigh64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const result = ctx.reg_alloc.ScratchGpr();
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(1));
code.MULLDO_(result, src_a, src_b);
ctx.reg_alloc.DefineValue(inst, result);
}
template<>
void EmitIR<IR::Opcode::UnsignedDiv32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const result = ctx.reg_alloc.ScratchGpr();
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(1));
code.DIVWU_(result, src_a, src_b);
code.RLDICL(result, result, 0, 32);
ctx.reg_alloc.DefineValue(inst, result);
}
template<>
void EmitIR<IR::Opcode::UnsignedDiv64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const result = ctx.reg_alloc.ScratchGpr();
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(1));
code.DIVDU_(result, src_a, src_b);
ctx.reg_alloc.DefineValue(inst, result);
}
template<>
void EmitIR<IR::Opcode::SignedDiv32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const result = ctx.reg_alloc.ScratchGpr();
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(1));
code.DIVW_(result, src_a, src_b);
code.EXTSW(result, result);
ctx.reg_alloc.DefineValue(inst, result);
}
template<>
void EmitIR<IR::Opcode::SignedDiv64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const result = ctx.reg_alloc.ScratchGpr();
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(1));
code.DIVD_(result, src_a, src_b);
ctx.reg_alloc.DefineValue(inst, result);
}
template<>
void EmitIR<IR::Opcode::And32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(1));
auto const tmp3 = ctx.reg_alloc.ScratchGpr();
auto const tmp8 = ctx.reg_alloc.ScratchGpr();
auto const tmp9 = PPC64::RNZCV;
code.RLDICL(tmp3, src_a, 0, 32); // Truncate
code.AND(tmp3, tmp3, src_b);
code.CNTLZD(tmp9, tmp3);
code.RLWINM(tmp8, tmp3, 0, 0, 0);
code.SRDI(tmp9, tmp9, 6);
code.SLDI(tmp9, tmp9, 30);
code.OR(tmp9, tmp9, tmp8);
ctx.reg_alloc.DefineValue(inst, tmp3);
}
template<>
void EmitIR<IR::Opcode::And64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(1));
auto const tmp3 = ctx.reg_alloc.ScratchGpr();
auto const tmp10 = ctx.reg_alloc.ScratchGpr();
auto const tmp9 = PPC64::RNZCV;
code.AND(tmp3, src_a, src_b);
code.CNTLZD(tmp10, tmp3);
code.SRADI(tmp9, tmp3, 32);
code.SRDI(tmp10, tmp10, 6);
code.RLWINM(tmp9, tmp9, 0, 0, 0);
code.SLDI(tmp10, tmp10, 30);
code.OR(tmp9, tmp9, tmp10);
ctx.reg_alloc.DefineValue(inst, tmp3);
}
template<>
void EmitIR<IR::Opcode::AndNot32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const result = ctx.reg_alloc.ScratchGpr();
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(1));
code.NAND_(result, src_a, src_b);
code.RLDICL(result, result, 0, 32);
ctx.reg_alloc.DefineValue(inst, result);
}
template<>
void EmitIR<IR::Opcode::AndNot64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const result = ctx.reg_alloc.ScratchGpr();
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(1));
code.NAND_(result, src_a, src_b);
ctx.reg_alloc.DefineValue(inst, result);
}
template<>
void EmitIR<IR::Opcode::Eor32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const result = ctx.reg_alloc.ScratchGpr();
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(1));
code.XOR_(result, src_a, src_b);
code.RLDICL(result, result, 0, 32);
ctx.reg_alloc.DefineValue(inst, result);
}
template<>
void EmitIR<IR::Opcode::Eor64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const result = ctx.reg_alloc.ScratchGpr();
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(1));
code.XOR_(result, src_a, src_b);
ctx.reg_alloc.DefineValue(inst, result);
}
template<>
void EmitIR<IR::Opcode::Or32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const result = ctx.reg_alloc.ScratchGpr();
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(1));
code.OR_(result, src_a, src_b);
code.RLDICL(result, result, 0, 32);
ctx.reg_alloc.DefineValue(inst, result);
}
template<>
void EmitIR<IR::Opcode::Or64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const result = ctx.reg_alloc.ScratchGpr();
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(1));
code.OR_(result, src_a, src_b);
ctx.reg_alloc.DefineValue(inst, result);
}
// TODO(lizzie): NZVC support for NOT
template<>
void EmitIR<IR::Opcode::Not32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const result = ctx.reg_alloc.ScratchGpr();
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
code.NOT(result, source);
code.RLDICL(result, result, 0, 32);
ctx.reg_alloc.DefineValue(inst, result);
}
template<>
void EmitIR<IR::Opcode::Not64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const result = ctx.reg_alloc.ScratchGpr();
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
code.NOT(result, source);
ctx.reg_alloc.DefineValue(inst, result);
}
template<>
void EmitIR<IR::Opcode::SignExtendByteToWord>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const result = ctx.reg_alloc.ScratchGpr();
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
code.EXTSB(result, source);
code.RLDICL(result, result, 0, 32);
ctx.reg_alloc.DefineValue(inst, result);
}
template<>
void EmitIR<IR::Opcode::SignExtendHalfToWord>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const result = ctx.reg_alloc.ScratchGpr();
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
code.EXTSH(result, source);
code.RLDICL(result, result, 0, 32);
ctx.reg_alloc.DefineValue(inst, result);
}
template<>
void EmitIR<IR::Opcode::SignExtendByteToLong>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const result = ctx.reg_alloc.ScratchGpr();
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
code.EXTSH(result, source);
ctx.reg_alloc.DefineValue(inst, result);
}
template<>
void EmitIR<IR::Opcode::SignExtendHalfToLong>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const result = ctx.reg_alloc.ScratchGpr();
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
code.EXTSB(result, source);
ctx.reg_alloc.DefineValue(inst, result);
}
template<>
void EmitIR<IR::Opcode::SignExtendWordToLong>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const result = ctx.reg_alloc.ScratchGpr();
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
code.EXTSW(result, source);
ctx.reg_alloc.DefineValue(inst, result);
}
template<>
void EmitIR<IR::Opcode::ZeroExtendByteToWord>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const result = ctx.reg_alloc.ScratchGpr();
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
code.RLWINM(result, source, 0, 0xff);
code.RLDICL(result, result, 0, 32);
ctx.reg_alloc.DefineValue(inst, result);
}
template<>
void EmitIR<IR::Opcode::ZeroExtendHalfToWord>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const result = ctx.reg_alloc.ScratchGpr();
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
code.RLWINM(result, source, 0, 0xffff);
code.RLDICL(result, result, 0, 32);
ctx.reg_alloc.DefineValue(inst, result);
}
template<>
void EmitIR<IR::Opcode::ZeroExtendByteToLong>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const result = ctx.reg_alloc.ScratchGpr();
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
code.RLWINM(result, source, 0, 0xff);
ctx.reg_alloc.DefineValue(inst, result);
}
template<>
void EmitIR<IR::Opcode::ZeroExtendHalfToLong>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const result = ctx.reg_alloc.ScratchGpr();
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
code.RLWINM(result, source, 0, 0xffff);
ctx.reg_alloc.DefineValue(inst, result);
}
template<>
void EmitIR<IR::Opcode::ZeroExtendWordToLong>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const result = ctx.reg_alloc.ScratchGpr();
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
code.RLDICL(result, source, 0, 32);
ctx.reg_alloc.DefineValue(inst, result);
}
template<>
void EmitIR<IR::Opcode::ZeroExtendLongToQuad>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
ASSERT(false && "unimp");
}
// __builtin_bswap32
template<>
void EmitIR<IR::Opcode::ByteReverseWord>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
if (false) {
//code.BRW(result, source);
//code.RLDICL(result, result, 0, 32);
} else {
auto const result = ctx.reg_alloc.ScratchGpr();
code.ROTLWI(result, source, 24);
code.RLWIMI(result, source, 8, 8, 15);
code.RLWIMI(result, source, 8, 24, 31);
code.RLDICL(result, result, 0, 32);
ctx.reg_alloc.DefineValue(inst, result);
}
}
// __builtin_bswap64
template<>
void EmitIR<IR::Opcode::ByteReverseHalf>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const result = ctx.reg_alloc.ScratchGpr();
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
if (false) {
//code.BRH(result, source);
code.RLWINM(result, source, 0, 0xff);
} else {
code.ROTLWI(result, source, 24);
code.RLWIMI(result, source, 8, 0, 23);
code.CLRLDI(result, result, 48);
}
ctx.reg_alloc.DefineValue(inst, result);
}
template<>
void EmitIR<IR::Opcode::ByteReverseDual>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
if (false) {
//code.BRD(result, source);
} else {
auto const tmp10 = ctx.reg_alloc.ScratchGpr();
auto const tmp9 = ctx.reg_alloc.ScratchGpr();
auto const tmp3 = ctx.reg_alloc.ScratchGpr();
code.MR(tmp3, source);
code.ROTLWI(tmp10, tmp3, 24);
code.SRDI(tmp9, tmp3, 32);
code.RLWIMI(tmp10, tmp3, 8, 8, 15);
code.RLWIMI(tmp10, tmp3, 8, 24, 31);
code.ROTLWI(tmp3, tmp9, 24);
code.RLWIMI(tmp3, tmp9, 8, 8, 15);
code.RLWIMI(tmp3, tmp9, 8, 24, 31);
code.RLDIMI(tmp3, tmp10, 32, 0);
ctx.reg_alloc.DefineValue(inst, tmp3);
}
}
// __builtin_clz
template<>
void EmitIR<IR::Opcode::CountLeadingZeros32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const result = ctx.reg_alloc.ScratchGpr();
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
code.CNTLZW(result, source);
ctx.reg_alloc.DefineValue(inst, result);
}
// __builtin_clz
template<>
void EmitIR<IR::Opcode::CountLeadingZeros64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const result = ctx.reg_alloc.ScratchGpr();
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
code.CNTLZD(result, source);
ctx.reg_alloc.DefineValue(inst, result);
}
template<>
void EmitIR<IR::Opcode::ExtractRegister32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::ExtractRegister64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::ReplicateBit32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::ReplicateBit64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::MaxSigned32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const result = ctx.reg_alloc.ScratchGpr();
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(0));
code.CMPD(powah::CR0, result, src_a);
code.ISELGT(result, result, src_b);
code.RLDICL(result, result, 0, 32);
ctx.reg_alloc.DefineValue(inst, result);
}
template<>
void EmitIR<IR::Opcode::MaxSigned64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const result = ctx.reg_alloc.ScratchGpr();
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(0));
code.CMPD(powah::CR0, result, src_a);
code.ISELGT(result, result, src_b);
ctx.reg_alloc.DefineValue(inst, result);
}
template<>
void EmitIR<IR::Opcode::MaxUnsigned32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const result = ctx.reg_alloc.ScratchGpr();
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(0));
code.CMPLW(result, src_a);
code.ISELGT(result, result, src_b);
code.RLDICL(result, result, 0, 32);
ctx.reg_alloc.DefineValue(inst, result);
}
template<>
void EmitIR<IR::Opcode::MaxUnsigned64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const result = ctx.reg_alloc.ScratchGpr();
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(0));
code.CMPLD(result, src_a);
code.ISELGT(result, result, src_b);
ctx.reg_alloc.DefineValue(inst, result);
}
template<>
void EmitIR<IR::Opcode::MinSigned32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const result = ctx.reg_alloc.ScratchGpr();
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(0));
code.CMPW(powah::CR0, result, src_a);
code.ISELGT(result, result, src_b);
code.RLDICL(result, result, 0, 32);
ctx.reg_alloc.DefineValue(inst, result);
}
template<>
void EmitIR<IR::Opcode::MinSigned64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const result = ctx.reg_alloc.ScratchGpr();
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(0));
code.CMPD(powah::CR0, result, src_a);
code.ISELGT(result, result, src_b);
ctx.reg_alloc.DefineValue(inst, result);
}
template<>
void EmitIR<IR::Opcode::MinUnsigned32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const result = ctx.reg_alloc.ScratchGpr();
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(0));
code.CMPLW(result, src_a);
code.ISELGT(result, result, src_b);
code.RLDICL(result, result, 0, 32);
ctx.reg_alloc.DefineValue(inst, result);
}
template<>
void EmitIR<IR::Opcode::MinUnsigned64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const result = ctx.reg_alloc.ScratchGpr();
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(0));
code.CMPLD(result, src_a);
code.ISELGT(result, result, src_b);
ctx.reg_alloc.DefineValue(inst, result);
}
} // namespace Dynarmic::Backend::RV64
@@ -0,0 +1,458 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include <powah_emit.hpp>
#include <fmt/ostream.h>
#include "dynarmic/backend/ppc64/a32_core.h"
#include "dynarmic/backend/ppc64/abi.h"
#include "dynarmic/backend/ppc64/emit_context.h"
#include "dynarmic/backend/ppc64/emit_ppc64.h"
#include "dynarmic/backend/ppc64/reg_alloc.h"
#include "dynarmic/ir/basic_block.h"
#include "dynarmic/ir/microinstruction.h"
#include "dynarmic/ir/opcodes.h"
namespace Dynarmic::Backend::PPC64 {
template<>
void EmitIR<IR::Opcode::FPAbs16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPAbs32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPAbs64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPAdd32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPAdd64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPCompare32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPCompare64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPDiv32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPDiv64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPMax32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPMax64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPMaxNumeric32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPMaxNumeric64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPMin32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPMin64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPMinNumeric32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPMinNumeric64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPMul32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPMul64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPMulAdd16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPMulAdd32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPMulAdd64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPMulSub16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPMulSub32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPMulSub64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPMulX32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPMulX64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPNeg16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPNeg32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPNeg64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPRecipEstimate16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPRecipEstimate32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPRecipEstimate64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPRecipExponent16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPRecipExponent32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPRecipExponent64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPRecipStepFused16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPRecipStepFused32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPRecipStepFused64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPRoundInt16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPRoundInt32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPRoundInt64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPRSqrtEstimate16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPRSqrtEstimate32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPRSqrtEstimate64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPRSqrtStepFused16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPRSqrtStepFused32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPRSqrtStepFused64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPSqrt32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPSqrt64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPSub32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPSub64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPHalfToDouble>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPHalfToSingle>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPSingleToDouble>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPSingleToHalf>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPDoubleToHalf>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPDoubleToSingle>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPDoubleToFixedS16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPDoubleToFixedS32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPDoubleToFixedS64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPDoubleToFixedU16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPDoubleToFixedU32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPDoubleToFixedU64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPHalfToFixedS16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPHalfToFixedS32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPHalfToFixedS64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPHalfToFixedU16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPHalfToFixedU32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPHalfToFixedU64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPSingleToFixedS16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPSingleToFixedS32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPSingleToFixedS64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPSingleToFixedU16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPSingleToFixedU32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPSingleToFixedU64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPFixedU16ToSingle>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPFixedS16ToSingle>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPFixedU16ToDouble>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPFixedS16ToDouble>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPFixedU32ToSingle>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPFixedS32ToSingle>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPFixedU32ToDouble>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPFixedS32ToDouble>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPFixedU64ToDouble>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPFixedU64ToSingle>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPFixedS64ToDouble>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPFixedS64ToSingle>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
} // namespace Dynarmic::Backend::RV64
@@ -0,0 +1,380 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include <powah_emit.hpp>
#include <fmt/ostream.h>
#include "dynarmic/backend/ppc64/a32_core.h"
#include "dynarmic/backend/ppc64/abi.h"
#include "dynarmic/backend/ppc64/emit_context.h"
#include "dynarmic/backend/ppc64/emit_ppc64.h"
#include "dynarmic/backend/ppc64/reg_alloc.h"
#include "dynarmic/ir/basic_block.h"
#include "dynarmic/ir/microinstruction.h"
#include "dynarmic/ir/opcodes.h"
namespace Dynarmic::Backend::PPC64 {
template<>
void EmitIR<IR::Opcode::SignedSaturatedAddWithFlag32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::SignedSaturatedSubWithFlag32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::SignedSaturation>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::UnsignedSaturation>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::SignedSaturatedAdd8>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::SignedSaturatedAdd16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::SignedSaturatedAdd32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::SignedSaturatedAdd64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::SignedSaturatedDoublingMultiplyReturnHigh16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::SignedSaturatedDoublingMultiplyReturnHigh32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::SignedSaturatedSub8>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::SignedSaturatedSub16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::SignedSaturatedSub32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::SignedSaturatedSub64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::UnsignedSaturatedAdd8>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::UnsignedSaturatedAdd16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::UnsignedSaturatedAdd32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::UnsignedSaturatedAdd64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::UnsignedSaturatedSub8>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::UnsignedSaturatedSub16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::UnsignedSaturatedSub32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::UnsignedSaturatedSub64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
// Packed
template<>
void EmitIR<IR::Opcode::PackedAddU8>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::PackedAddS8>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::PackedSubU8>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::PackedSubS8>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::PackedAddU16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::PackedAddS16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::PackedSubU16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::PackedSubS16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::PackedAddSubU16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::PackedAddSubS16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::PackedSubAddU16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::PackedSubAddS16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::PackedHalvingAddU8>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::PackedHalvingAddS8>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::PackedHalvingSubU8>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::PackedHalvingSubS8>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::PackedHalvingAddU16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::PackedHalvingAddS16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::PackedHalvingSubU16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::PackedHalvingSubS16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::PackedHalvingAddSubU16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::PackedHalvingAddSubS16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::PackedHalvingSubAddU16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::PackedHalvingSubAddS16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::PackedSaturatedAddU8>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::PackedSaturatedAddS8>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::PackedSaturatedSubU8>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::PackedSaturatedSubS8>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::PackedSaturatedAddU16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::PackedSaturatedAddS16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::PackedSaturatedSubU16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::PackedSaturatedSubS16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::PackedAbsDiffSumU8>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::PackedSelect>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
// Crypto
template<>
void EmitIR<IR::Opcode::CRC32Castagnoli8>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::CRC32Castagnoli16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::CRC32Castagnoli32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::CRC32Castagnoli64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::CRC32ISO8>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::CRC32ISO16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::CRC32ISO32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::CRC32ISO64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::AESDecryptSingleRound>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::AESEncryptSingleRound>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::AESInverseMixColumns>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::AESMixColumns>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::SM4AccessSubstitutionBox>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::SHA256Hash>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::SHA256MessageSchedule0>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::SHA256MessageSchedule1>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
} // namespace Dynarmic::Backend::RV64
File diff suppressed because it is too large Load Diff

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