Commit Graph
251 Commits
Author SHA1 Message Date
Henrik RydgårdandClaude Opus 5.5 9de0f6e45d bltzal and friends test rs from before they link
As on hardware (cpu/cpu_alu/branch_link_ra), with ra as rs the branch
sees its old value, not the return address. Every core linked first.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 09:55:35 +02:00
Henrik RydgårdandClaude Opus 5.5 480ebbe601 VFPU: vfim converts its half float like vh2f
As on hardware (cpu/vfpu/vfim), a denormal half becomes a signed zero,
and inf and NaN keep their mantissa bits in place. The interpreter got
denormals half their value, the IR the IEEE one.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 09:55:35 +02:00
Henrik RydgårdandClaude Opus 5.5 73f1b9465a Interpreter: Vector loads ignore the D prefix's write mask
As on hardware (cpu/vfpu/prefix_load), lv.q, lvl.q and lvr.q write every
lane whatever mask a pending D prefix has, and leave the prefix pending,
like lv.s and mtv already did. The IR cores fall back to the interpreter
for lvl.q and lvr.q, so they get this too.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 09:55:35 +02:00
Henrik RydgårdandClaude Opus 5.5 66b26d08c1 IR: Leave the dot products with prefix constants past the vector size to the interpreter
On the PSP, lanes past the vector size where both the S and T prefixes are
constants add their product to vdot, and an S constant there is added in by vfad
and vavg (cpu/vfpu/prefix_lanes). The interpreter does that; the IR dropped those
lanes. Such prefixes are rare, so the IR leaves them to the interpreter.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 21:11:03 +02:00
Henrik RydgårdandClaude Opus 5.5 2e3b844cff Add a headless armhf cross build, for testing ARM32 under qemu
./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>
2026-10-14 17:32:53 +02:00
Henrik RydgårdandClaude Opus 5.5 1f5e7e275b test.py: minmax_zero and minmax_tie pass now
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 11:59:35 +02:00
Henrik RydgårdandClaude Opus 5.5 51ce3fbde9 test.py: shift_encodings and allegrex_encodings pass now
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 10:56:48 +02:00
Henrik Rydgård 9b07e78604 Merge remote-tracking branch 'origin/master' into cpu-equiv-1
# Conflicts:
#	pspautotests
2026-10-14 01:22:31 +02:00
Henrik RydgårdandClaude Opus 5.5 d0567e2dfb GE: Nest CALLs only two deep, like the hardware
- Fixes #7395

The GE has two return address registers, and a CALL beyond them does
nothing: the list carries on after it. A signal CALL is done by the
firmware, which saves and clears them, so it starts the count over,
and the signal RET restores it. Shown on a PSP by the new
gpu/callstack/depth test.

Soul Calibur: Broken Destiny turns lighting on for its weapon trails
from a third-level CALL, so on hardware the trails stay unlit and take
their colors from the vertices. We ran that CALL and drew them white.

Plain CALLs now store a 0 base address in their stack entry, which a
signal CALL never does, so the depth can be counted without changing
the savestate layout.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 00:54:01 +02:00
Henrik RydgårdandClaude Opus 5.5 19d9f57b70 test.py: Add the CPU equivalence hardware tests to tests_next
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 00:10:00 +02:00
Henrik Rydgård 4e2d3ab8d0 Merge remote-tracking branch 'origin/master' into loadmodule-deferred 2026-10-11 20:00:41 +02:00
Henrik RydgårdandClaude Opus 5.5 2f21ca6837 Savedata, sceIo: Behave like hardware with the memory stick removed
With "Memory Stick inserted" off, the savedata modes that show no dialog now
fail with their own "no memory stick" error, as recorded on a PSP with the
stick pulled out (pspautotests utility/savedata/nomemstick). ms0: opens, stats
and directory opens fail with SCE_KERNEL_ERROR_NODEV.

SOCOM: Fireteam Bravo 2's boot-time AUTOLOAD loaded its save anyway, or said
there was no data rather than no memory stick.

test.py gets a test_flags table for tests that need their own headless flags;
this one runs with --no-memstick.

Fixes #21846

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 19:30:39 +02:00
Henrik RydgårdandClaude Opus 5.5 4fda53a62e sceKernelLoadModule: Put the module in memory when the wait ends
We copied the module into memory at the start of the call, then made the caller
wait for the read. On hardware the module's bytes only arrive at the end, and
until then other threads can use that memory (pspautotests modules/loadrace).

SOCOM: Fireteam Bravo 2 EU (UCES00543) relies on this. Its loader EBOOT loads
SOCOMPSP.PRX on one thread while another builds a GU display list in a 0x40-byte
buffer at the end of its .bss. That list overruns into the free memory right after
it, which is where the game module goes. On hardware the module load later
overwrites the overrun; for us, the overrun overwrote the module's first
instructions, and the game crashed as soon as it started.

The load now runs from a CoreTiming event at the end of the wait. Pending loads
are saved in savestates.

Fixes #18842

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 19:08:11 +02:00
Henrik RydgårdandClaude Opus 5.5 eb7fdf1c2d Add the modules/unloadfill and video/mpeg/deletetiming tests
Both in tests_next. unloadfill's fill matches (text becomes break 1,
data and bss 0xFFFFFFFF, as we already do), but sceKernelQueryModuleInfo
reports a text_size 6 words larger than the PSP's. deletetiming's
sceMpegDelete timing matches (it reschedules, in the 20-60 ms range); the
rest of the mpeg setup has older differences.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 20:33:34 +02:00
Henrik RydgårdandClaude Opus 5.5 834e617cc1 SoftGPU: Carry UVs and normals into formats without them as the GE does
gpu/vertices/carry, recorded on a PSP: a draw whose format has no UVs uses the last ones read, by any
earlier draw in draw order, textured or not. They're kept as read and get the scale and offset of the
draw that uses them. Normals carry over the same way.

We kept the UVs after scaling, only read them with texturing on, and after a cached indexed draw
carried the highest vertex instead of the last one drawn. The carried values move from statics in
ReadVertex to TransformUnit.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-09 02:03:31 +02:00
Henrik RydgårdandClaude Opus 5.5 21bf2b878e Add gpu/stencil/writemask to the test list
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 11:28:40 +02:00
Henrik RydgårdandClaude Opus 5.5 251d42ab2e SoftGPU: Leave textures with an invalid level address to the generic samplers
The x86 sampler JIT returned zero for a whole sample when a texture level
pointer was null, and for a mipmapped texture it checked the next level
too, even when it isn't blended in. Tactics Ogre draws its font from a
mipmapped texture whose next level has an invalid address, at level 0,
so all its text was missing on x86-64. The generic samplers read a null
level's texels as zero only where they use that level, and still apply
the texture function. Now samplers for such textures aren't compiled,
and the JIT's null checks are gone. Test: gpu/textures/mipinvalid.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-05 13:05:46 -06:00
Henrik RydgårdandClaude Opus 5.5 27ce1be6e9 Module start/stop threads: Honor the whole SceKernelSMOption
sceKernelStartModule, sceKernelStopModule and the self-stop calls now
check their option first: with SDK 2.08 or later its size must be 0x14,
and attribute bits outside 0x00F06000 are an error.

The option's attribute is added to the module's thread attribute rather
than ignored (or, when stopping, replacing it), and the default attribute
is 0 rather than the module's own attribute. A nonzero mpidstack puts the
stack in that partition, which has to be user-accessible for a user
module and kernel-only for a kernel module.

A failed thread creation is now returned instead of asserting.

Hardware test: modules/startoptions.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-05 10:10:22 -06:00
Henrik RydgårdandClaude Opus 5.5 5de2a5070f test.py: Add the gpu/exact tests
Nine tests of GE probe jobs that the software renderer reproduces bit for
bit (transform, planes, coverage, lines, texture, lighting, curves, nearclip,
pixel), recorded on a PSP.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 15:09:43 -06:00
Henrik RydgårdandClaude Opus 5.5 4f1b5a0ae9 test.py: Move 23 GE tests that pass now to tests_good, add gpu/clipping/xycull
Updates pspautotests for xycull and the PSP replayer's hang fixes.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 15:09:43 -06:00
Henrik RydgårdandClaude Opus 5.5 2ba6438daf test.py: gpu/depth/precision and transformprecision pass now
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 15:09:42 -06:00
Henrik RydgårdandClaude Opus 5.5 d7625c6952 Add the gpu/depth/transformprecision test
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 15:09:42 -06:00
Henrik RydgårdandClaude Opus 5.5 1772448638 HLE: Don't delay results in an interrupt handler
hleDelayResult and Sas's delay put the wait on whatever thread was
current, which in an interrupt handler is the idle thread it runs on.
With both idle threads waiting there's nothing left to schedule, the
same crash as sceDisplaySetMode in Gods Eater Burst's vblank handler.

Where pspautotests intr/delays shows what hardware returns there,
match it: sceIoOpen and __sceSasCore fail with ILLEGAL_CONTEXT,
sceMpegCreate with 80618008, and sceAtracReinit can deinit but not set
up the codec (807f00fd).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-01 15:49:26 -06:00
Henrik RydgårdandClaude Opus 5.5 9162592493 SoftGPU: Add the secondary color on the portable triangle path
Without SSE or NEON, triangle pixels got the secondary color in place of
the primary one plus it, so lit triangles came out black. It showed as
the "unexplained" known failures on riscv64 and loongarch64, and broke
the new gpu/lighting/shademap there. Reproduced on arm64 by building
without NEON.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-30 15:47:16 -06:00
Henrik RydgårdandClaude Opus 5.5 2771244a32 Add the gpu/lighting/specular and shademap tests
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-30 15:47:16 -06:00
Henrik RydgårdandClaude Opus 5.5 3ac43a70a6 Add the utility/savedata/shutdownstatus test
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-30 09:58:37 -06:00
Henrik RydgårdandClaude Opus 5.5 d86896bed9 Tlspl: Time out at once like other waits
Recorded on hardware (pspautotests threads/tls/timeout), a Tlspl
allocation follows the same timeout rule as the other waits, including
failing at once for 0 and 1us without writing the timeout back, which the
shared rule it moved to in the last commits didn't give it yet. Before
that it waited the raw timeout, ~30us short.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-30 09:06:30 -06:00
Henrik RydgårdandClaude Opus 5.5 02b2b7b699 sceDisplay: Release vblank waiters ~50us after the vblank
On hardware a thread waiting for vblank returns ~53us after it, where we
had it back in ~5us, and the first of four waiters runs after ~85us: each
waiter beyond the first adds ~9us (pspautotests threads/scheduling/
vblankwake). The waiters are now released by a separate event 48us + 9us
per extra waiter after the vblank. Which vblank a wait is for is still
decided at the vblank, so a thread that starts waiting in between still
waits a whole frame (sceDisplay section version 8).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-30 09:06:30 -06:00
Henrik RydgårdandClaude Opus 5.5 f26ca37dad Threads: Context switches cost what they do on hardware
Timed on a PSP, each way of handing the CPU to another thread (the call
and the switch together):

                                     hardware   before   now
  rotate to an equal thread              7        14       7
  signal, better thread runs            10        17      10
  it waits again, back to caller        10        19      12
  wakeup, better thread runs             8        13       6
  it sleeps again, back to caller        7        12       6
  start a better thread, entry          30        28      30
  thread ends, back to its waiter       21        13      20
  notify, better thread's callback      14        13      14

A switch between two threads now costs 1150 cycles instead of 2700.
Starting a better thread costs 2000 cycles more, ending a thread 3300,
and setting up a callback 1800.

Also splits a wait timeout's ~30us into the deadline being taken 12us
into the call and the timeout going off 18us after it. That only changes
the time left written back, which threads/semaphores/wait and
threads/fpl/cancel pin between them. intr/vblank is re-recorded so it
no longer depends on the phase of the frame.

threads/callbacks/combos now passes.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-30 09:06:30 -06:00
Henrik RydgårdandClaude Opus 5.5 f5b4fd739d test.py: Add threads/scheduling dispatchwake and mutexhandoff
Both already pass.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-30 09:06:30 -06:00
Henrik RydgårdandClaude Opus 5.5 a17a9eb147 Callbacks: A notify takes the thread out of its CB wait right away
On hardware, notifying a callback of a thread in a CB wait takes it out of
the wait at once, even though the callback only runs when the thread would
get the CPU. A semaphore signalled in between doesn't end the wait: the
callback runs first, then the wait resumes and takes it (pspautotests
threads/callbacks/combos). We left the thread on the wait list until the
callback started, so the signal ended the wait and the callback didn't
run.

The notify now pauses the wait, as starting a callback used to. If the
callbacks are canceled before the thread's turn comes, the wait just
resumes (Thread savestate section version 7).

threads/callbacks/combos goes in the to-do list: a callback returning to
the thread that notified it still takes ~13us where hardware takes ~9,
part of the context switch cost.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-30 09:06:30 -06:00
Henrik RydgårdandClaude Opus 5.5 fb9ac8397e Threads: Wait timeouts work like hardware's, one rule for all of them
Every wait with a timeout behaves the same on hardware (pspautotests
threads/scheduling/waittimeouts). The deadline is taken, and the alarm set
up a moment later. If the deadline has passed by then, the wait fails with
WAIT_TIMEOUT at once, without yielding or writing the timeout back. That's
usual for 0us, half the time for 1us, and rare after; AllocateVpl does more
first. Otherwise it ends max(t, 205us) + ~35us after the call. Each object
had its own guess (24/245, 25/250, 20/250 and so on), and only MsgPipe had
the immediate case.

__KernelWaitTimesOutAtOnce() and __KernelWaitTimeoutUs() now do it for
semaphores, event flags, mutexes, lwmutexes, mbx, msgpipes, fpl, vpl and
WaitThreadEnd. The latency past the deadline isn't counted in the time
left written back.

Outcomes that hardware decides by the clock's phase (these, and
sceKernelDelayThread returning at once) go with the likelier one. Ones
between 50% and certain are instead spread evenly over calls, so a polling
loop can't lock into never yielding (sceKernelThread section version 7).
This replaces the pseudo-random choice for delays.

Also adds threads/scheduling/readyqueue, which already passes.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-30 09:06:30 -06:00
Henrik RydgårdandClaude Opus 5.5 80b7d27c12 Add the audio/atrac/c0mono and audio/audiocodec/at3param tests
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-29 17:14:03 -06:00
Henrik RydgårdandClaude Opus 5.5 178186ef4e sceVaudio: Reserving the channel waits about 250us
On hardware sceVaudioChReserve takes ~260us once it gets past the busy
check, succeed or fail, and worse threads can run meanwhile. Releasing
takes ~25us and doesn't wait. We returned at once, which is what
audio/sceaudio/reserve's [r] markers showed; it now passes.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-29 11:09:47 -06:00
Henrik RydgårdandClaude Opus 5.5 69e10728c9 test.py: Add gpu/primitives/indices32 to the to-do list
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-29 11:09:47 -06:00
Henrik RydgårdandClaude Opus 5.5 af8088b790 Threads: A delay can end before it starts waiting
A delay's deadline is now + usec, and the clock is read again when the
alarm is set. If the deadline has passed by then, the call returns 0 at
once without giving up the CPU. On hardware that makes
sceKernelDelayThread(0) return at once about 60% of the time. On a thread's
first wait after it starts, a delay of 1 does so about two times in three
as well (pspautotests threads/scheduling/delayzero). We always waited at
least 210us.

The choice is pseudo-random off the tick count, not the tick phase, since
our cycle counts are regular enough for a polling loop to lock into never
yielding. Threads remember whether they've waited since starting (Thread
savestate section version 6).

Also moves threads/vpl/create into the passing tests: re-recorded on 6.61,
it agrees with what we do for partitions 8 and 9.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-29 11:09:47 -06:00
Henrik RydgårdandClaude Opus 5.5 13dee85375 sceIo: host0: behaves like usbhostfs with dispatch suspended
With dispatch suspended, IO fails in the driver when it tries to wait. The
memory stick driver returns SCE_KERNEL_ERROR_CAN_NOT_WAIT, while usbhostfs,
which serves host0: under PSPLink, returns -1. host0: is mostly what
homebrew developers run from, so it now does the same.

Also from threads/scheduling/dispatch, which now passes:
- sceIoRead reports an async operation still in progress before failing
  on suspended dispatch.
- A write to stdout or stderr doesn't give up the CPU.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-29 11:09:47 -06:00
Henrik RydgårdandClaude Opus 5.5 68dbbb253d sceDisplay: Vblank lasts 770us, not 731.5us
Measured with pspautotests display/vblanklen: 730-770us from
sceDisplayWaitVblankStart returning to the end of vblank, with an hcount
of up to 14 inside it. The old value dated from the first source drop
and left the highest hcount at 13. display/hcount now passes (with the
test fixed not to depend on where a line boundary falls).

Booting 75 games against master shows no difference.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-29 09:19:32 -06:00
Henrik RydgårdandClaude Opus 5.5 436db85cf5 Reschedule on IO completion, drop the reschedule in time queries
The time queries rescheduled since 2013, so that a game spinning on the
clock would let a thread that a timing event had woken run (it fixed
audio in Crimson Gem Saga and Where Is My Heart?). In 2014 audio and
delay wakeups started rescheduling themselves, but IO completion never
did, and a movie reader thread in Driver 76 was only getting in through
the time queries. Now IO completion dispatches like any other wakeup.

The PSP doesn't dispatch in a time query, and doing so let a thread
that a terminate woke run too early. threads/threads/terminate now
passes.

Checked by booting 75 games against master: the same in all of them,
with Asphalt Urban GT2 getting further in the same time.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-29 09:19:32 -06:00
Henrik RydgårdandClaude Opus 5.5 23fc0cd422 Interrupts: Refuse sub-interrupt handlers where the firmware does
Only the GE and vblank interrupts take user sub-interrupt handlers, and
vblank only in slots 0-15, with some of the rest already held by the
kernel. The errors follow interruptman.prx's checks, and which interrupts
have handlers at all is read back from pspautotests intr/registersub and
intr/releasesub, which now pass.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-29 09:19:32 -06:00
Henrik RydgårdandClaude Opus 5.5 5674c789ef sceUmd: Match hardware's parameter checks and wait timeouts
- A timeout of 0 to sceUmdWaitDriveStatWithTimer/CB means no timeout,
  not a tiny one (or 8ms for the CB version).
- Timeouts round like the event flag wait does.
- A wait with no timeout no longer times out right after a callback.
- sceUmdRegisterUMDCallBack only accepts callbacks.
- sceUmdActivate requires the name to be exactly "disc0:", and it and
  sceUmdDeactivate/sceUmdGetDiscInfo reject kernel pointers.
- sceUmdDeactivate needs a name in mode 2.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-29 09:19:32 -06:00
Henrik Rydgård eab0b53a7a Merge pull request #22371 from hrydgard/audiocodec-fixes
sceAudiocodec and sceVideocodec timing and Atrac3+ fixes
2026-09-28 17:26:04 -06:00
Henrik RydgårdandClaude Opus 5.5 c162eb3d74 sceAudiocodec: Match hardware setup, framing, errors and timing; fix Atrac3 polarity
Checked against pspautotests audio/audiocodec, recorded on a PSP.

- Atrac3+: at3Related selects headered (mpeg.prx) or raw (libatrac3plus)
  frames, instead of sniffing for the sync word. The header's size field
  is 10 bits, as the context's. Header errors 0x211/0x213, bad frames
  0x20a, all returning SCE_AVCODEC_ERROR_INVALID_DATA with nothing read.
- The first successfully decoded Atrac3+ frame, and the first two AAC
  frames, produce no output. Checked sample-for-sample against hardware.
- Atrac3: the parameter at 0x28 selects the frame layout, as
  libatrac3plus.prx's table maps it. We used to read its low bit as a
  joint-stereo flag, which decoded mono (0x0F) streams as stereo garbage.
  AtracCtx2 had the table's fields swapped the same way.
- at3_standalone's Atrac3 output was inverted relative to the PSP's
  (sceAtrac too). Negate the IMDCT scale.
- CheckNeedMem sizes (AAC is 0x658c), codec 0x1004/0x1005, Init
  validation (AAC sample rate, Atrac3 parameter, Atrac3+ channels), and
  ReleaseEDRAM clearing edramAddr.
- Every call that reaches the ME now blocks for its measured time, and
  decode time is modelled per codec and frame size.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 16:57:41 -06:00
Henrik RydgårdandClaude Opus 5.5 c11e46aeea LwMutex: Take the lock after a callback if it was released during it
The end callback checked the kernel object's lockThread, which for an
lwmutex is only refreshed by sceKernelReferLwMutexStatus. The lock state
lives in the workarea, so an unlock during the callback left the waiter
waiting forever. Verified against pspautotests threads/lwmutex/callbacks.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 15:59:25 -06:00
Henrik RydgårdandClaude Opus 5.5 eda92c4ef2 Callbacks: Track callback nesting per thread, one level deep
Replaces the global in-callback counter with each thread's own mipscall
chain, so several threads can be inside callbacks at once, and other
threads' callbacks (better priority ones right away) run while one is.
Verified against pspautotests threads/callbacks/otherthread, recursion
and intrnotify:

- A callback nests only one level: a CB wait that would go deeper never
  returns on hardware, so the callback is left pending instead.
- A non-CB wait inside a callback no longer runs callbacks because of
  the CB wait the callback interrupted.
- Callbacks for a waiting thread are only taken when it beats both the
  running thread and every ready one. After an interrupt (which runs on
  the idle thread) that's the thread about to resume, not idle.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 15:59:25 -06:00
Henrik RydgårdandClaude Opus 5.5 acd2738b7a Callbacks: Deliver to other threads by priority, fix sceKernelCancelCallback
Verified against pspautotests threads/callbacks/delivery:

- Notifying the callback of a better priority thread in a CB wait runs
  it right away. Callbacks of other waiting threads stay pending until
  those threads would get to run, rather than being taken at any
  reschedule, so they can still be counted or canceled.
- sceKernelCancelCallback clears the notify count, not just the arg.

threads/callbacks/cancel, count and umd/wait/wait now pass.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 15:59:25 -06:00
Henrik RydgårdandClaude Opus 5.5 3bf8b5b5c9 Callbacks: Run nested callbacks from CB waits, match hardware ordering
Verified against new pspautotests threads/callbacks/afterwait and nested:

- A thread inside a callback runs its own pending callbacks (even the
  same one again) nested, when it enters a CB wait. Waits paused by a
  nested callback are keyed by the outer callback's id.
- sceKernelCheckCallback inside a callback returns ILLEGAL_CONTEXT
  without running anything.
- sceKernelSleepThreadCB with a queued wakeup runs pending callbacks
  before consuming it.
- A thread whose wait ended during a callback keeps the CPU, instead of
  queueing behind threads of the same priority.

threads/callbacks/notify now passes too.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 15:59:25 -06:00
Henrik RydgårdandClaude Opus 5.5 44d0c401d5 test.py: Give Debug builds three times the wall-clock timeout
gpu/clut/offset and gpu/commands/material take over 1s each in a Debug
build, and every so often pushed past the 5s limit mid-run, which reads
as a failure with truncated output.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 11:03:53 -06:00
Henrik RydgårdandClaude Opus 5.5 9f182a2a1b Update pspautotests: compact rendertarget test output
gpu/rendertarget/copy no longer prints a million pixels one at a time,
and runs in 0.17s under the interpreter rather than ~4.5s.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 10:32:42 -06:00
Henrik RydgårdandClaude Opus 5.5 6dffcc91d2 Utility: Check request sizes like the firmware
InitStart sizes: Netconf and NpSignin accepted any size, then wrote
common.size bytes back from a 64-68 byte host struct, copying host memory
into PSP RAM. GamedataInstall looked for install files before checking the
size, and the HtmlViewer read options before checking the whole request was
in memory. All the dialogs now check the address, then the sizes
sceUtility_Driver accepts (utility/dialog/sizes), before anything else, as
the firmware does (a bad address is INVALID_ADDRESS), and write back no more
than the struct.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-25 12:50:41 -06:00