The backend follows the ARM64 one: a dispatcher, a register cache on top of the IR one, and
native exits, syscalls, breakpoints and transfers. Everything else goes to the IR interpreter
for now. Floats are mapped one lane per S register (no SIMD mapping yet), and addresses are
masked, as on every 32-bit build.
The JIT core now uses it on ARM32 in place of the old ArmJit.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
./b.sh --armhf builds headless and the unit tests for 32-bit ARM, which run under qemu-arm the
way the loongarch64 and riscv64 builds do. setup-cross.sh learns the target (it also needs an
EGL stub), and the docs cover setting it up without root.
hash/hash dies with a bus error under qemu-arm on every CPU core, so it goes in a known-failures
list for the target.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A flush stores pending imms through SCRATCH1, so in a non-constant MemoryCheck, and in
ValidateAddress's bad-address path, the address computed into SCRATCH1 could be replaced by an imm
before IRRunMemCheck or ReportBadAddress saw it. It now waits in SCRATCH2, which flushes don't use.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
When floats are mapped one lane at a time (RISC-V and LoongArch), a NOINIT dest was only upgraded
to a loaded one when a source had the same first reg. In Vec2Pack32To16 vf3, vf2,vf3 the dest is
the source's second lane, so it could be mapped without a load and the op read garbage. Found
with an IR-based ARM32 JIT, which failed vi2s.q C002, C000 in cpu/vfpu/overlap because of it.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The encodings are reachable from guest code, and the CPUEquivalence fuzzer generates them, which
stopped a debug-build "UnitTest all" at the interpreter's assert. Every core already leaves rt
alone for mfv and writes nothing for mtv there (the IR interprets them), so only the asserts go.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Their IR-based replacements stay. The JIT core now creates the IR JIT on both, so "JIT using
IR" is the same as "JIT" on every architecture (ARM32 still uses its legacy JIT for both). The
settings no longer offer it, a saved JIT_IR loads as JIT, and the JIT harness and CPUEquivalence
fuzzer drop the duplicate run.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Every mirrored branch was triggering the full libretro build matrix and
tying up their shared runners. Requested by the libretro team.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Where the cores now agree, what the hardware tests answered, what's still
open (the NaN handling waits on the cpu-equiv-4 branch), and the ARM32 cores
as measured on a Galaxy S8+ and A21.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
mtvc masks the written control register like the other JITs, vsgn tests the
exponent (so a denormal gives 0, as in the interpreter), and the hardware
half-to-float conversion quiets NaNs, so vh2f goes to the interpreter.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The ARM32 JIT only checked for unknown prefixes, so a swizzle naming a lane
past the op's size, or a T prefix on vscl, read the wrong register or
asserted in VMOV. It now uses the ARM64 JIT's gates, and sends what they
refuse to the interpreter.
Its vasin was a polynomial approximation, not the bit-exact function the
other cores use (cpu/vfpu/exact failed 364 lines), so it goes to the
interpreter too, like vsin and vcos already did.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ins with msb below pos encoded an invalid BFI instead of doing nothing,
as the interpreter and the other JITs do, and ext with pos + size past 32
an invalid UBFX. max and min of two constants compared them unsigned.
Found by CPUEquivalence.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
So vcmovt never moves and vcmovf always moves (cpu/vfpu/undefined). The
interpreter did nothing for either, and the JITs took it as imm3 = 6,
one CC bit per lane.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On the PSP, zeros and denormals all compare equal, a tie returns the
second operand, and the result keeps its bits (cpu/vfpu/minmax_zero,
minmax_tie, fcr31). The interpreter got the ties and NaNs right but
compared denormals as numbers, and each JIT had its own fast path with
host min/max, which returned the first operand on a tie and flushed or
not depending on the host's mode.
All cores now compare by vfpu_minmax_key(), an integer key that is 0
for a zero exponent and orders the rest by sign and magnitude, and
select the operand by it, without branching on NaN.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
When a search stopped matching the focused game, it stayed focused while
hidden, so its background and music kept going.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Their old grid setting is no longer migrated, so everyone gets the new
view there. Recent and Remote still keep theirs.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The old GridView keys are now read as ints, so a missing key can be told
apart from false. Configs without them get the new defaults, while
existing ones still migrate their choice.
Folders get taller lines in the large list too, though not as tall as
the games.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
FocusGame cleared the path to focus right after setting it, so since
the refresh became deferred nothing got refocused. The refresh also
showed every game again while the search bar still showed the filter.
Now an existing button is focused directly, without a refresh, and a
refresh reapplies an active search filter.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
srl/srlv rotate when the low bit of their unused field (rs or sa) is
set and shift when it's clear, whatever the other bits hold
(cpu/cpu_alu/shift_encodings). The interpreter treated values above 1 as
invalid and the other cores shifted.
The Allegrex bit ops decode only some bits of sa, so all 32 values are
one of wsbh, wsbw, seb, seh and bitrev (cpu/cpu_alu/allegrex_encodings).
AllegrexBitOp() gives the canonical one for every core. Special3
function 0x18 does nothing on the PSP, where it was decoded as an alias
of 0x20.
The interpreter dispatch is regenerated, and its generator now lists
each instruction name once. CPUEquivalence no longer skips the srl/srlv
encodings.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
"Grid icon size" is renamed to "Icon size", keeping the translations with
"grid" dropped from them. New keys for the view mode choices.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
In the large list, right and left move between a game and its info
button, so up and down still step from game to game.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Each line in the large list gets an info button, doing the same as a long
press or right click.
Holding the accept button now works as a long press in all view modes,
so it can be done from a gamepad.
Rename "Grid icon size" to "Icon size", since it now also applies to the
large list.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- Fixes#22202
A third view mode next to grid and list: a list with full size icons,
scaled by the grid icon size setting. Like the XMB, the selected (or
hovered) game plays its ICON1.PMF in place of the icon.
The per-tab grid bools become GameBrowserView1-4 ints. The old GridView
keys are still read, to pick the default, but no longer saved.
The PMF decoding moved out of GameScreen's PMFView into a shared
PMFVideoTexture.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
It was left out of the JIT harness tests in 2024 for a crash nobody tracked
down, and runs fine on both arm64 and x86-64 Macs now.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
They swap only the rounding mode bits in FPSCR now instead of resetting it,
which kept FZ off, so a denormal input is flushed like on the other hosts
(cpu/fpu/denormal).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
round.w.s gave 0 for a NaN instead of 0x7FFFFFFF like the other
conversions. round.w.s, ceil.w.s and floor.w.s reset FPSCR to
round-to-nearest and left it there, so cvt.w.s and the float ops after
them ignored the guest's mode (cpu/fpu/roundmode).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Runs FPU math after a ctc1, and after a mode set from outside on blocks
compiled before it, on every core with and without ops left to the
interpreter, and checks the result against the interpreter. A fake syscall
checks that HLE always runs in the host's own mode.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Merges the settings in an ini file over headless's own defaults, for runs that
need something like JitDisableFlags. Unlike in the app, the result is never
saved.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A native backend can run this op through its generic fallback after its own
asm applied the guest's mode, which RestoreHostRoundingMode() doesn't know
about, so it has to clear the bits even when it didn't save them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A JIT only switched modes around syscalls, and only took the mode into account
in conversions, once it had compiled a ctc1 (hasSetRounding). A mode from an
interpreted ctc1, a savestate made on another core, a core switch or a thread
switch went unnoticed, and HLE then ran in the guest's mode. UpdateFCR31()
now sets the flag when fcr31 isn't the default, and clears the block cache at
the next safe point, since blocks compiled before don't switch either. A new
JIT gets an UpdateFCR31() too.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Interpreted ops run in the guest's mode. A syscall left to the interpreter
(with JitDisable flags, say) is host code and now gets the host's mode, like a
compiled one, and an interpreted ctc1 to fcr31 now takes effect right away:
nothing set hasSetRounding for it, so the mode only reached the host at the
next time slice. Every other interpreted op skips the two calls it used to
make after it. The IR frontend's Comp_Generic, which the IR interpreter and the
IR native backends share, gets the same treatment.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
RestoreHostRoundingMode read the control register back to clear our bits, even
when nothing had been applied. Now ApplyHostRoundingMode saves the host's mode
the first time it changes it, and restore writes that back only if it did. With
the guest in the default mode, both are now just a flag check.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ARM32's libm ceilf/floorf don't honor flush-to-zero, so the interpreters flush
the input themselves there.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ARMv7 NEON always flushes denormals and rounds to nearest, whatever FPSCR
says, so on ARM32 the scalar paths are used, which follow the guest's mode
like everything else.
The Vec4 ops only come from VFPU code, and the VFPU itself always rounds to
nearest and flushes denormals (cpu/vfpu/fcr31, see docs/cpu-equivalence.md),
so the NEON paths were arguably closer to the PSP there. But they disagreed
with every other host and core, and agreement between those comes first.
Running VFPU ops in the VFPU's own mode on every core would get both, and is
still to do.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>