Commit Graph
49379 Commits
Author SHA1 Message Date
Henrik RydgårdandClaude Opus 5.5 7625f7529a Debugger: Say in "deferred" when a ticketed reply follows
input.buttons.press, gpu.stats.get, gpu.record.dump and the game.state.*
operations with wait answer later with a message carrying the request's
ticket. Their deferred acknowledgement carried that ticket too, and wsdbg
took it for the answer, so a waited save that failed exited 0 and --sync
moved on before it was done.

Handlers that answer this way now call ReplyLater(), and the deferred event
says "replyFollows": true. wsdbg keeps waiting for the reply in one-shot
mode, under --sync and for :nowait lines. An error reply or a missing one
to a :nowait line now fails the run, a raw line can't reuse a ticket that
is still pending, and auto-assigned tickets skip past ones a raw line chose.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-21 09:34:27 +02:00
Henrik Rydgård c4fd1a9020 Merge pull request #22697 from hrydgard/savestate-load-rollback
SaveState: Go back to the current state when a load fails partway
2026-10-21 09:11:57 +02:00
Henrik Rydgård a51fe51055 Merge pull request #22690 from hrydgard/me-core
Media Engine part 1: Address space and core (off by default)
2026-10-21 08:37:22 +02:00
Henrik RydgårdandClaude Opus 5.5 ccde03984f SaveState: Go back to the current state when a load fails partway
The state that load undo kept was snapshotted before every slot load anyway.
Take it before every load, and if the load fails partway, load it back rather
than resetting the game. Load undo still gets it, now written after the load,
and not when the load was rolled back, so the undo on disk stays the useful one.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-21 08:30:59 +02:00
Henrik Rydgård 9b37b63294 Merge pull request #22695 from hrydgard/savestate-restart-fixes
EmuScreen: Restart after a failed savestate load before drawing the frame
2026-10-21 08:18:28 +02:00
Henrik RydgårdandClaude Opus 5.5 7f70918def AuCtx: Accept a savestate with an MP3 handle reserved without parameters
sceMp3ReserveMp3Handle(0) gives a context with no buffers, which the PCM
buffer size check added to AuCtx::DoState rejected, failing the load partway
through.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-21 07:52:21 +02:00
Henrik RydgårdandClaude Opus 5.5 deb9693f6a EmuScreen: Restart after a failed savestate load before drawing the frame
The restart shut the GPU down after PrepareCopyDisplayToOutput had bound its
pipelines in the open backbuffer pass, which trips the pipelinesToCheck_
assert in VulkanRenderManager::CheckNothingPending. The failed load already
stops the frame from running, so nothing is lost by restarting first.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-21 07:24:56 +02:00
Henrik Rydgård 23a5f317bd Merge pull request #22691 from hrydgard/play-crash-fixes
Fix crashes from 1.21-beta1: near-plane clip overflow, stale non-buffered mode, 32-bit memory map
2026-10-20 23:54:36 +02:00
Henrik RydgårdandClaude Opus 5.5 8e2bb1faed 32-bit: Keep the memory view range reserved, and mask guest addresses to it
Find4GBBase found 256 MB of free address space for the views and then
unmapped it, so the gaps between the views went back to the system. On a
32-bit Android device, a running game had malloc heaps between VRAM and RAM
and GPU driver buffers just past RAM, and masked guest addresses (bits 30-31
cleared) reach a whole GB above base, mostly heap. A stray guest access
landed in those silently instead of faulting, and another thread could even
map something where a view was about to go, which the view's MAP_FIXED then
replaced. That fits the 32-bit-only crashes deep in system libraries and in
our own heap structures.

Keep the range reserved as PROT_NONE and map the views over it, putting the
reservation back when a view is released. Mask out bits 28-29 too on 32-bit
(no valid address has them), so accesses stay inside the reservation.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-20 23:21:11 +02:00
Henrik RydgårdandClaude Opus 5.5 a5e5fc079a GPU: Pick up a changed Skip Buffer Effects even without a render resize
The framebuffer manager only switches between buffered and non-buffered
rendering on a render resize. Turning on Auto FrameSkip clears Skip Buffer
Effects without posting one, so after resuming, EmuScreen ran the buffered
path with a GPU still in non-buffered mode. A frame displayed from RAM was
then drawn straight to output with no render pass bound, crashing in
VulkanRenderManager::SetScissor. Treat a mismatch as a resize in
BeginHostFrame, whatever changed the setting.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-20 23:03:00 +02:00
Henrik RydgårdandClaude Opus 5.5 d3a23e94e9 Software transform: Don't write past the vertex buffer when clipping against the near plane
ClipTrianglesAgainstNearPlane took limits for the transformed vertices and
the output indices but never checked them, and the caller passed constants
(one of them a typo, 65336). Each partially clipped triangle adds two
vertices, so a large draw with many triangles crossing the near plane ran
off the end of transformed_ and crashed. Pass the real limits, and drop
triangles that don't fit.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-20 23:01:24 +02:00
Henrik RydgårdandClaude Opus 5.5 fef84cf0ca CPU: Add the Media Engine core, off by default
MeCore owns a second MIPSState in the ME's address space, held in reset
until released (SetReset). It runs on the interpreter, a slice behind the
main CPU: CoreTiming runs it for the cycles the main CPU has just used, in
Advance and ForceCheck. Its own PRId (COP0 22) and mfic/mtic flag; MMIO
counts as kernel mode on it, and a syscall stops it with an error rather
than reaching HLE. The interpreter's block stepping reads delay slots
through the stepping CPU's memory, and cache instructions log at verbose
level, as ME code flushes its caches in a loop.

Created at boot only for homebrew (PBP with MG category or no SFO, ELF;
the VSH counts, as vshmain.prx loads as an ELF), when "Enable ME
emulation for homebrew" is on: a global checkbox in developer settings,
off by default and not translated yet, or --me-core. Saved in its own
section after MMIO (SaveStart v5), with its own memory; loading a state
creates or removes the ME to match.

The boot page now starts with a reset stub that sends the ME on to
0xBFC00040, where homebrew puts its code (from the firmware's reset code,
read on a PSP-1000).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-20 22:22:40 +02:00
Henrik RydgårdandClaude Opus 5.5 41851007a2 Memory: Give the Media Engine an address space of its own
The ME sees the main CPU's RAM, but has its own eDRAM at 0 (with
0x80000000 and 0xA0000000 mirrors) where the main CPU has its scratchpad,
its own memory where the main CPU has VRAM, and both CPUs share the boot
page at 0xBFC00000.

- The ME's own memory is views in the main arena flagged MV_ME_ONLY, which
  only Memory::CreateMESpace() maps, into a second reservation along with
  the shared views at their same arena positions. It gets its own block
  shadow.
- The eDRAM (MV_EDRAM) is the ME's half of the eDRAM chip, the GE's VRAM
  being the other: 2MB on the PSP-1000 (whose resident ME image says so),
  4MB on the others (from mcidclan's libmecore, not hardware-checked).
- At VRAM's addresses, through 0x44000000 and 0xA4000000 alike, the ME
  doesn't see VRAM but memory of its own (pspautotest me/vram, on a
  PSP-1000): where homebrew keeps the VME's buffers. Its size isn't known;
  2MB with VRAM's mirrors here, and no depth layout on 16-bit accesses.
- 0xA0000000 reaches physical memory uncached, so RAM is mapped at
  0xA8000000 too.
- The boot page is a view too, which the main CPU reaches through a new
  MMIO device (in savestates from MMIO section version 2). The ME runs
  code from it, uncached, so the main CPU's writes invalidate code there.
  On masked platforms it isn't mapped, and the device uses a plain
  buffer.
- AddressSpace has IsValidAddress etc, with the ME's rules when edramSize
  is set, and the interpreters and the IR interpreter check and fetch
  instructions through their state's space. The IR compiler's checks still
  use the main CPU's, until the ME gets a JIT.
- On Darwin, MemArena can hold several view reservations.

Masked platforms (iOS, 32-bit) don't get an ME space yet.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-20 22:22:40 +02:00
Henrik RydgårdandClaude Opus 5.5 f269abe20e pspautotests: me/vram
What the ME sees at VRAM's addresses, recorded on a PSP-1000. In the
next set: it needs the ME on, and Sony's ME firmware to hand the ME back
to at the end.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-20 22:22:40 +02:00
Henrik Rydgård 1a0659cb97 Merge pull request #22688 from hrydgard/pops-cherry-pick
Cherry-pick some general commits from the POPS PR
2026-10-20 22:12:11 +02:00
Francisco José García García 3cda5fcea5 Screenshot: Write the PNG when handed an empty buffer
Save8888RGBAScreenshot() calls png_image_write_to_memory() with the buffer
it's given, or with none when it's empty, which only measures the PNG. That
call succeeds too, so it returned a buffer of the right size, all zeros. Its
only caller so far passes one that's big enough.
2026-10-20 21:49:46 +02:00
Francisco José García García d037ca1b7b PBPReader: Read part of a subfile
GetSubFile() reads a whole subfile, and refuses those over 256MB.
ReadSubFile() reads a range of one, for the PSAR of a PS1 EBOOT, which holds
its disc image.
2026-10-20 21:49:46 +02:00
Francisco José García García 3d69708fca sceIo: PGD offset through a pointer, and plain data from that offset
ioctl 0x04100002 (set the PGD offset) gets a pointer to the offset with
inlen 4 from the firmware's POPS manager. Our own sceNpDrmEdataSetupKey still
passes the offset itself.

When ioctl 0x04100001 finds no PGD there, the file is read as plain data from
that offset, not from the start (the same for ordinary files, whose offset is
0). A converted PS1 EBOOT's disc image is plain, and POPS reads it right
after. A PGD with the wrong key still leaves the file at 0.
2026-10-20 21:49:46 +02:00
Francisco José García García e9515d0f8a HLE: 6.60 kernel NIDs for module loading, memset and strnlen
The firmware's POPS manager imports these by their 6.60 NIDs:
sceKernelLoadModuleForKernel and sceKernelStartModule from ModuleMgrForKernel,
SysMem's sceKernelMemset and sceKernelGetUIDcontrolBlock, Sysclib's strnlen,
and sceKernelExtendKernelStack, which runs the callback like
sceKernelExtendThreadStack does.

sceKernelGetUIDcontrolBlock reports no such UID: the manager uses it to make
a descriptor it opened in kernel mode usable from user mode, and ours aren't
access-checked.
2026-10-20 21:49:42 +02:00
Henrik Rydgård e99106e5e7 Merge pull request #22689 from hrydgard/homebrew-hle
HLE and CPU bits for homebrew that runs code on the Media Engine
2026-10-20 21:43:59 +02:00
Henrik RydgårdandClaude Opus 5.5 ad40eec12e CPU: cfc0 and ctc0
COP0's control registers, which the firmware's exception code (and
ProTrekkr's Media Engine stub, clearing $14 and $15) keeps state in: 32
words next to cop0 in MIPSState, saved from section version 6.
Interpreted, as the other COP0 instructions are.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-20 21:18:21 +02:00
Henrik RydgårdandClaude Opus 5.5 3ec2c5369b IR: Find blocks to invalidate by walking the table for huge ranges
sceKernelIcacheInvalidateAll invalidates 0..0x3FFFFFFF, which is a
million 1 KB pages, each looked up in the hash map. A homebrew calling
it once per frame spent most of its time there. When the range has more
pages than the table has entries, go through the table instead.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-20 21:18:21 +02:00
Henrik RydgårdandClaude Opus 5.5 7475563a3b HLE: Register ExceptionManagerForKernel, sceCtrlSetButtonIntercept
ExceptionManagerForKernel's table was never registered, so kernel
modules importing sceKernelRegisterDefaultExceptionHandler (LightMP3's
exception.prx) were left with an unresolved import. It now holds only
that one: registering the unimplemented entries would take them from
the firmware's exceptionman.prx under --vsh. Nothing could have saved
the old layout, since it was never registered.

sceCtrl_driver's sceCtrlSetButtonIntercept hides buttons from user mode
readers, which Daedalus does with the system buttons games don't get
here anyway: a no-op. Daedalus's Media Engine build needs both.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-20 21:18:21 +02:00
Henrik RydgårdandClaude Opus 5.5 0a8d569c47 HLE: kuKernelCall and sctrlHENFindFunction
Custom firmware calls, which homebrew that runs ME code uses to reach
its own kernel modules: KUBridge's kuKernelCall runs a function with the
first eight of its arguments and stores v0/v1 in ret1/ret2 afterwards;
sctrlHENFindFunction (SystemCtrlForKernel, and a new SystemCtrlForUser)
finds a loaded module's export by module, library and NID.

Tanaka's Touhou ports find and call their GE4 wrapper's exports with
them.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-20 21:18:21 +02:00
Henrik RydgårdandClaude Opus 5.5 54d59c600b sceGe: sceGeEdramGetHwSize and sceGeEdramSetSize
The calls a PSP-2000 and later switch to their 4MB of VRAM with. We have
the PSP-1000's 2MB, and answer as it does, recorded in the new
gpu/ge/kernel/edramsize. Tanaka's Touhou ports import them in their GE4
wrapper module, and fall back to 2MB.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-20 21:18:21 +02:00
Henrik RydgårdandClaude Opus 5.5 b09d84d1ae Kernel: A kernel module's threads may have priorities 0x01-0x7E
User modules stay at 0x08-0x77. Recorded on a PSP-1000 in the new
threads/threads/kernel/priority. ProTrekkr's ME test, a kernel module,
creates its audio thread at priority 2.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-20 21:18:21 +02:00
Henrik RydgårdandClaude Opus 5.5 c5d2adb303 IR: Compile data cache ops to nothing
The interpreter does nothing for any cache op but icache invalidation, but
the IR only knew four of them and sent the rest to the interpreter one at
a time. Media Engine code flushes its data cache with a loop of 0x14s,
often every pass of its main loop: with the ME emulated (on a branch),
mcidclan's me-simple-count went from 15.9 to 1.0 seconds for 3 emulated
ones.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-20 21:18:21 +02:00
Henrik RydgårdandClaude Opus 5.5 6ce91d4bdb scePower: Implement scePowerLock and scePowerUnlock as no-ops
They keep the PSP from suspending while held, which never happens here.
mcidclan's vme-primary-dmac and PMP Mod call them.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-20 21:18:21 +02:00
Henrik Rydgård 47c97b92aa Merge pull request #22683 from hrydgard/address-space
CPU: Give each MIPSState an address space for its memory accesses
2026-10-20 21:12:37 +02:00
Henrik RydgårdandClaude Opus 5.5 11296d32b1 CPU: Give each MIPSState an address space for its memory accesses
Memory::AddressSpace is where a CPU's addresses point (base) and its block
shadow, with the unchecked accessors and block shadow helpers as methods. The
main CPU's is Memory::g_mainSpace, kept in step with Memory::base and
blockShadow, and MIPSState::space points at it.

The CPU cores now go through their state's space instead of the globals: the
interpreter's and IR interpreter's loads and stores (which also saves the IR
interpreter reloading the global base), the IR interpreter's block lookup,
the block table's shadow entries, and the native JITs' memory base register,
block shadow lookups and pointerified registers. Address validity and
everything outside the cores still use the globals.

No change for the main CPU; this is so another CPU (the Media Engine) can see
memory differently. Benchmarked against master (God of War: Chains of
Olympus, Tekken 6): JIT instructions retired within noise, IR interpreter
2% fewer.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-20 18:41:28 +02:00
Henrik Rydgård 2b4a7ba5a2 Merge pull request #22684 from hrydgard/misc-fixes
Fix Font= in language inis, misc
2026-10-20 18:25:29 +02:00
Henrik Rydgård fb2310e6ea Merge pull request #22685 from hrydgard/hle-mipsstate-plumbing
HLE: Pass the MIPSState to syscalls and their implementations
2026-10-20 18:25:13 +02:00
Henrik RydgårdandClaude Opus 5.5 f99f05a40e x64 IR JIT: Cast the replacement function pointer for clang
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-20 18:08:17 +02:00
Henrik RydgårdandClaude Opus 5.5 135e752105 Windows: Let the language ini's Font override use system fonts again
- Fixes #22675

Since 3aa4714ce0 desktop Windows draws text with DirectWrite, which looked
up every font in a collection of only our bundled fonts, so an override
like "Font = 微软雅黑" silently fell back to Roboto. Fall back to the
system font collection when the family isn't one of ours.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-20 16:40:37 +02:00
Henrik Rydgård 4f71d62c28 Frameskipping is a gross hack, so move it in under "Speed hacks" 2026-10-20 16:32:44 +02:00
Henrik Rydgård f36b458bbf Crashfix in the framedump recorder 2026-10-20 16:32:44 +02:00
Henrik RydgårdandClaude Opus 5.5 7dd1475ed7 HLE: Pass the MIPSState to syscalls and their implementations
HLEFunc and ReplaceFunc now take the MIPSState they run on. The dispatch
paths (both interpreters and every JIT backend) pass in the state they
belong to, and CallSyscall*, hleFinishSyscall and the HLE mips call
machinery use it instead of currentMIPS.

The wrappers in FunctionWrappers.h take the function as `auto` and call it
through HLECall, which also accepts an implementation with an extra leading
MIPSState * parameter and passes the state to it. Any other signature still
fails to compile, as before. PARAM/RETURN now refer to a local `mips`.

The HLE functions (and replacement functions) that used currentMIPS now take
the state that way. Callers outside a syscall's own frame - CoreTiming
events, the adhoc matching thread, dialogs, PPGe, the debugger UIs - pass
currentMIPS or &mipsr4k explicitly. The kernel's thread internals still use
currentMIPS.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-20 15:45:14 +02:00
Henrik Rydgård 394c05f414 Merge pull request #22682 from hrydgard/spline-line-edges
Splines: Draw a line patch's shared quad edges once
2026-10-20 14:52:25 +02:00
Henrik RydgårdandClaude Opus 5.5 7adf0c15ee Splines: Draw a line patch's shared quad edges once
A patch drawn as lines is a zigzag per quad (gpu/probe exp175), but the GE
draws a quad's right edge only in the patch's last column, since the next
quad's left edge is the same line. Edges between patches are still drawn
twice. Opaque lines can't show the difference; NFS Most Wanted's
alpha-blended spline visualizer (#22104) came out brighter wherever the
edges were drawn twice. gpu/probe exp257 counts the draws per pixel with
additive blending, for Bezier and spline patches; both are now exact, as is
the NFS frame against the PSP.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-20 14:11:06 +02:00
Henrik Rydgård 5471f5fe88 Merge pull request #22681 from hrydgard/mmio-jit
Compile kernel loads/stores (MMIO under the JITs), sceKernelRegisterIntrHandler, and an eret hardware test
2026-10-20 14:08:39 +02:00
Henrik RydgårdandClaude Opus 5.5 9b1399b789 CPU: eret is checked on a PSP now
cpu/cop0/eret, recorded on firmware 6.61, has eret go to ErrorEPC and clear
only ERL with Status.ERL set, otherwise to EPC clearing EXL, with no delay
slot. Int_Eret already does that; the test joins tests_good.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-20 13:40:47 +02:00
Henrik RydgårdandClaude Opus 5.5 5f2e4bd416 Kernel: Implement sceKernelRegisterIntrHandler and friends
sceKernelRegisterIntrHandler, sceKernelReleaseIntrHandler,
sceKernelEnableIntr and sceKernelDisableIntr now keep a base handler per
interrupt, and return what tests/intr/kernel/intrhandler recorded on a PSP:
80020065 for an interrupt number out of range (or no handler), 80020067 for
a second handler, 80020068 for one that isn't there.

Faking success for these used to stall the VSH boot, which is why they were
left unresolved. It doesn't anymore: the firmware's drivers make the same 31
calls (interrupts 4, 12, 15-18, 20-24 and 31), and 6.61 reaches the XMB on the
JIT and the interpreter. Nothing raises those interrupts yet, so the handlers
are only kept, and saved in savestates (IntrHandler section version 2).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-20 13:40:47 +02:00
Henrik RydgårdandClaude Opus 5.5 53ac2c7c56 IR: Compile kernel loads and stores instead of interpreting them
Kernel code's loads and stores may reach hardware registers, which only the
interpreter handled, so the IR frontend sent every one of them (in kernel
blocks, only run by the VSH boot) to it.

They now become LoadKernel*/StoreKernel* IR ops (one per size, like the
plain loads and stores), which check at run time whether the address is
memory or a hardware register (MMIO). After constant
propagation, ResolveKernelMemory turns the ones at a constant address that's
plain memory into ordinary loads and stores, so only the hardware registers,
and addresses only known at run time, stay checked. The native JITs run the
two ops through their IR fallback. They're barriers, since a register access
has side effects, but can't change the GPRs, so constants stay known across.

The kernel mode check for MMIO moves to the interpreter, the only caller that
can run user code there. TestMMIO covers the new paths and which accesses
stay checked. Under --vsh, the JIT and the interpreter now make the same
unhandled register accesses, in the same numbers.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-20 13:40:47 +02:00
Henrik Rydgård aba3a99888 Merge pull request #22679 from hrydgard/exit-on-game-exit
Android frontends: --pause-menu-exit exits when the game ends, Args extra with URIs, Copy deep link
2026-10-20 13:25:53 +02:00
Henrik Rydgård 79e88a7e0d Merge pull request #22680 from hrydgard/cop0-mmio
CPU: COP0 per MIPSState, eret, and an MMIO device registry
2026-10-20 13:12:28 +02:00
Henrik RydgårdandClaude Opus 5.5 55f02684f5 Android: Enable "Copy deep link" on the game screen, copying the content URI
PPSSPP has access to the URI, so a frontend or adb can launch the game with it,
which isn't true of a plain path on modern Android.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-20 12:42:07 +02:00
Henrik RydgårdandClaude Opus 5.5 b7b4a265b7 MMIO: Register hardware register blocks as devices
The interpreter's MMIO dispatch was a hardcoded chain for GPIO and the syscon
serial port. Devices now register an address range in Core/HW/MMIO and get
each access with its width and the MIPSState making it, for registers that
answer each CPU differently. An access no device handles is logged and reads
as poison, as before.

GPIO and syscon serial become the first two devices. They're reset at boot,
which they never were, and saved in savestates (SaveStart section version 4).

ll/sc, lwl/lwr and lwc1/swc1 reach MMIO in the interpreter too, and kernel
blocks leave lwc1/swc1 to it like the other loads and stores. swl/swr and
lv/sv still treat it as a bad address. TestMMIO runs a kernel-mode access on
each core and the registers through a savestate.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-20 12:36:52 +02:00
Henrik RydgårdandClaude Opus 5.5 99335adb40 CPU: Keep COP0 in MIPSState, and interpret eret
The COP0 register file was a static array in the interpreter, shared by
anything that ran kernel code and lost on a savestate load. It's now part of
MIPSState and saved with it (MIPSState section version 5).

eret returns to EPC, or to ErrorEPC with Status.ERL set, as MIPS32 defines
it; that isn't checked against a PSP yet. It and halt carry a new ENDS_BLOCK
flag: the IR frontend ends the block after them and exits to whatever PC the
interpreter left, and the interpreter's IR-matching block timing stops there
too. TestCPUEret runs it on the interpreter, IR interpreter and JIT.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-20 12:36:52 +02:00
Henrik Rydgård 46a2d6a5b6 Merge pull request #22678 from hrydgard/ir-jit-restructure
IR JITs: Split the IR interpreter out, and keep IR to it
2026-10-20 12:36:20 +02:00
Henrik Rydgård 58f2eae3b4 Merge pull request #22677 from hrydgard/texreplace-fake-mip
Texture replacement fixes: fake mipmap levels, case-insensitive packs, cache sizing
2026-10-20 12:35:41 +02:00