Commit Graph
32237 Commits
Author SHA1 Message Date
dougchansan 7be9ad8607 recomp: AArch64 translation, host bridge and execution coverage
The AArch64 -> C emitter and the ArmRecomp bridge that hosts it.

Instructions translated beyond the original set: EXTR/ROR, ADC/ADCS/SBC/SBCS,
LDPSW, the load/store exclusives including the pair forms, CLREX, PRFM/PRFUM as
the hints they are, MRS/MSR for FPCR/FPSR, CNTPCT/CNTVCT/CNTFRQ, CTR_EL0, and
the DC cache-maintenance family.

Three of those were not gaps but wrong answers:

  Exclusives used to become a plain load/store with STXR unconditionally
  reporting success, which is exact only when nothing else can touch the
  address. Under real guest threads that makes every compare-and-swap
  non-atomic. They now route through Core::ExclusiveMonitor, the same monitor
  the fallback JIT uses, so a lock taken in recompiled code is visible to a
  thread on the JIT.

  FPCR/FPSR had nowhere to live, so a guest rounding mode was silently reset to
  the host default and dropped again on every transition to the JIT.

  The counter and CTR_EL0 are read from the emulator's own sources rather than
  invented, so the two engines cannot disagree about time or cache geometry
  across a transition.

Values that are constants here (CNTFRQ, CTR_EL0) and operations with nothing to
do (the DC clean/invalidate family - suyu never sets Dynarmic's
hook_data_cache_operations, so the JIT compiles them to nothing too) are
answered inline. DC ZVA and the IC family deliberately still fall back: one
writes memory, the other invalidates the instruction cache, which suyu acts on.

Coverage instrumentation on both sides. Static: a per-module
recomp_coverage.json with emitted/unhandled counts grouped by encoding and
signature. Runtime: blocks executed, SVCs, transitions in each direction split
by cause, and histograms of unimplemented opcodes, uncovered PCs and SVC
numbers. The static histogram turned out to be a poor predictor of what
executes - the three largest runtime gaps found so far appeared nowhere in the
static top twenty - so the runtime figures are what the work is prioritised
against.
2026-09-10 08:25:32 -10:00
dougchansan 5ed63bbe0a feat: suyu-cmd --probe-isa-list reports each title's CPU architecture
The static recompiler is AArch64-only, so whether a title is A32 or A64 decides
whether it can be targeted at all. Answering that by booting is slow and, for
titles that fail to boot, impossible.

--probe-isa-list walks XCI -> secure NSP -> Program NCA -> ExeFS -> main.npdm for
each path in a list file and writes a TSV. One process handles a whole library:
no Vulkan, no emulation, no crash risk.

The Program NCA is looked up under TitleType::Update as well as Base. An
update-only NSP has no Base entry at all, so it came back "no exefs" and its
NPDM was never read - which matters because an update can change the answer:
the target title ships an ARM32 base whose 4.0.0 update is AArch64, and
reading only the base reports the whole title as untargetable.
2026-09-10 08:23:38 -10:00
dougchansan 1f807645f5 diag: identify the NPDM by content hash, and name the update's provider slot
Size is not an identity. the target title's 2.4.0 and 4.0.0 updates both have
a 1476-byte main.npdm, one ARM32 and one AArch64, so a log line printing only the
size cannot say which of them a given code path loaded. Treating size as an
identity sent an investigation into which update gets applied down six dead ends;
an FNV-1a of the file bytes separated them in a single run.

The NPDM line also carries the ISA up front, which decides whether a title can
use the AOT path at all, and the ACI title id.

PatchExeFS logs which ContentProviderUnion slot answered for the update. The
union checks SysNAND, UserNAND, SDMC, FrontendManual, External in that order and
the frontend fills FrontendManual with the booted file's own NCAs, so an on-cart
update can answer even when a newer one is installed. Naming the slot is the
difference between knowing that and guessing at it.
2026-09-10 08:23:09 -10:00
dougchansan 97473f00a5 fix: export the update ExeFS, not the base
PatchManager replaces the ExeFS wholesale when an update is present, so the code
the emulator actually runs is the update's. The AOT exporter read the base NCA
regardless, and the resulting image diverged from live execution the moment an
updated title was booted - on a second title, at block 153.

Look up the Program NCA under the update title id and TitleType::Update first,
falling back to the base when no update exists.
2026-09-10 08:23:07 -10:00
dougchansan d390658972 fix: installed updates and DLC in NAND were never indexed
RegisteredCache::GetFileAtID tries eight layout variants and skipped every odd
index except 7:

    if ((i % 2) == 1 && i != 7) continue;

The .cnmt.nca suffix was therefore only ever tried at the cache root, never
inside a 000000XX/ directory - which is exactly where meta NCAs are stored, per
GetRelativePathFromNcaID's own format string for that case and per what
InstallEntry writes. So every meta NCA in NAND was unreachable, ProcessFiles
bailed at `file == nullptr` before registering anything, and no installed update
or DLC ever entered the cache.

Silently, too: a miss is indistinguishable from nothing being installed, which
is why the frontend's installed-title listing reports zero.

With the cache empty, PatchManager::PatchExeFS fell through the
ContentProviderUnion to the FrontendManual slot - which the frontend fills with
the booted file's own NCAs - and applied whatever update the cartridge happens
to carry, ignoring anything installed.

Fixing that exposed a second defect immediately behind it. ProcessFiles keys its
metadata map by title id, so two installed versions of the same title collide,
and the winner was whichever the directory walk reached last - no version
comparison anywhere. Now the higher GetTitleVersion() is kept.

Found on the target title, which ships an ARM32 base with an ARM32 on-cart
update while its AArch64 update sits in NAND: the title booted 32-bit and the
installed update was simply invisible.
2026-09-10 08:22:49 -10:00
dougchansan d11f29609c fix: RomFS registration was silently dropped
The registration map used emplace, which is a no-op when the key already exists.
A process re-registering its RomFS therefore kept the stale entry rather than
replacing it, and the title panicked on boot.

insert_or_assign is what was meant: the newest registration wins.
2026-09-10 08:22:35 -10:00
dougchansan 7e28ba98b7 fix: service handler registration dropped most commands
RegisterHandlers passed a FunctionInfoTyped<T> array straight to
RegisterHandlersBase, which walks it as FunctionInfoBase[]. The two layouts do
not match. sizeof() agrees, so a size assertion passes and tells you nothing,
but the member offsets differ: dumping IpcController's table showed entry 0 with
its name at offset 24 and entry 1 with its name at offset 20, with entry 2's key
landing at byte 60 rather than 64. Every element after the first was read from
the wrong place.

The result was silent and severe. IpcController registered 2 of its 6 handlers,
because the corrupted keys collided and emplace kept only the distinct ones.
QueryPointerBufferSize (command 3) was among the lost, and it is part of CMIF
session setup - so every title stalled during early service initialisation and
never reached the graphics stack at all.

Index with the typed pointer instead, one element at a time, via a new
single-entry RegisterHandlerBase. The array forms are kept for callers that
genuinely have a FunctionInfoBase array.
2026-09-10 08:22:33 -10:00
dougchansan fa086bf8db fix: AOT image dispatch resolved every PC to the wrong module
Two defects in the recompiled-image dispatcher, either of which alone makes the
AOT path execute nothing.

recomp_image_lookup subtracts the module base itself, so the dispatcher was
passing an already-relative PC and the subtraction happened twice. Every lookup
underflowed and missed, and the whole image silently ran on the JIT instead.

The module-name table had only four entries {rtld, main, subsdk0, sdk}. Switch
load order is rtld, main, subsdk0..subsdk9, sdk, so any title with more than one
subsdk mismapped: the module at index 3 is subsdk1, but a four-entry table hands
it the sdk image, and every module from index 4 on got no image at all. Super
a second title loads nine modules - four were left uncovered and one was given the
wrong image.

The owner search now also picks the image with the greatest base not exceeding
the PC, rather than asking every image in turn. Asking each in turn silently
returned a different module's block whenever two modules defined something at
the same offset, which they always do at offset 0.
2026-09-10 08:22:18 -10:00
suyu team d1d09321d7 feat: build the libretro core for Windows as well as Linux
Only the Linux .so was produced, so no suyu_libretro.dll ever reached the release. RetroArch loads .dll on Windows and .so on Linux, so both are shipped.
2026-09-04 23:14:34 +01:00
suyu team d850bb223a chore: remove all workflows except release
autofix.ci opened PRs referencing pre-rewrite history, and every extra workflow auto-fired on push, creating runs attributed to the pusher. ci.yml also hardcoded the original repository name.
2026-09-04 19:23:41 +01:00
suyu team 06b5a73366 fix: use _pkg/ instead of dist/ to avoid repo dist/ collision in packaging
All build jobs were using dist/ as staging dir, which collides with
the repo's own dist/ folder containing resources/icons/translations.
This caused libretro tar.gz to contain 625 resource files instead of
just suyu_libretro.so. Renamed libretro artifact for clarity.
2026-09-04 19:18:46 +01:00
suyu teamandClaude Opus 4.6 6a9f673d67 fix: mkdir -p in Android Collect APKs step
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-09-03 23:01:54 +01:00
suyu teamandClaude Opus 4.6 1b01193240 fix: GCC -Werror=shadow in dynarmic headers
Rename constructor params that shadow member variables in
location_descriptor.h, a64_location_descriptor.h,
microinstruction.h, and terminal.h.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-09-03 20:06:35 +01:00
suyu teamandClaude Opus 4.6 2f96a0714f fix: PowerShell mkdir in Windows package step
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-09-03 19:30:39 +01:00
suyu teamandClaude Opus 4.6 be1c4522f4 fix: add all boost sub-library include dirs to boost_headers target
CPM downloads boost with per-library include directories but boost_headers
only pointed at its own empty include dir. This adds all libs/*/include
dirs and creates Boost::boost alias for dynarmic compatibility.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-09-03 17:21:56 +01:00
suyu teamandClaude Opus 4.6 a9af4f3fdd fix: suppress Clang warnings — unused vars, variable shadowing
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-09-03 16:25:27 +01:00
suyu teamandClaude Opus 4.6 f3c5593bdd fix: enable PIC for libretro builds (shared library TLS relocation)
Boost.Asio thread-local storage needs -fPIC when linked into
suyu_libretro.so.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-09-02 22:30:40 +01:00
suyu teamandClaude Opus 4.6 6f73fd4e7e fix: revert Metal enum addition, remove Metal case from suyu_cmd switch
Metal renderer is WIP — removing the enum value avoids -Wswitch errors
in 11+ files. The MTL window class still compiles on Apple but isn't
selectable at runtime.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-09-02 22:18:10 +01:00
suyu teamandClaude Opus 4.6 dfd6578967 fix: port emu_window_sdl2_mtl.cpp from SDL2 to SDL3 API
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-09-02 21:57:40 +01:00
suyu teamandClaude Opus 4.6 8d59505f2c fix: macOS build — metal-cpp cmake target, Metal enum, unused var
- Add externals/metal-cpp/CMakeLists.txt (INTERFACE lib for Metal C++ headers)
- Wire metal-cpp into externals/CMakeLists.txt under if(APPLE)
- Add Metal to RendererBackend enum (used by suyu_cmd on Apple)
- Suppress unused 'props' warning on macOS in emu_window_sdl2_vk.cpp

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-09-02 21:37:48 +01:00
suyu teamandClaude Opus 4.6 a587affafd fix: suppress VMA warnings for both GCC and Clang
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-09-02 21:05:26 +01:00
suyu teamandClaude Opus 4.6 31bc930092 fix: suppress -Wconversion in VMA third-party header
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-09-02 20:31:35 +01:00
suyu teamandClaude Opus 4.6 4925386d8e fix: macOS runner 14→15 for std::stop_token (AppleClang 16)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-09-02 20:10:42 +01:00
suyu teamandClaude Opus 4.6 8b4dc45d54 fix: Boost::variant→headers, libboost-all-dev for filesystem/context/etc
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-09-02 19:56:15 +01:00
suyu teamandClaude Opus 4.6 35cfbc1217 ci: capture cmake configure logs on failure for debugging
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-09-02 19:42:34 +01:00