Skip to content

Fix boot hangs from BIOS-only CD search and IOP module loading paths - #359

Open
AlenDavid wants to merge 91 commits into
ran-j:mainfrom
AlenDavid:fix/cd-layer-search-file
Open

AlenDavid wants to merge 91 commits into
ran-j:mainfrom
AlenDavid:fix/cd-layer-search-file

Conversation

@AlenDavid

@AlenDavid AlenDavid commented Oct 18, 2026 •

Copy link
Copy Markdown

Closes #358

Three fixes, one per commit, for the boot hangs described in the issue:

  • sceCdLayerSearchFile: new runtime handler that forwards to sceCdSearchFile. The first two arguments are the same, and host files have no disc layers, so the layer argument is ignored. Added to the call list and the Ghidra export list, so the analyzer binds it automatically.
  • sceSifLoadModuleBuffer: IopEmulator::loadModuleBuffer now reads the ELF from IOP RAM, where sceSifSetDma put it. If there's no ELF there it still falls back to EE RAM, so hosts and tests that stage images in EE memory keep working. It also logs a warning when neither memory holds an image, where before it failed silently.
  • sceSifSearchModuleByAddress: new runtime handler backed by a new IopSubsystem::findModuleByAddress, which looks the address up in the emulator's module table (KSEG addresses are masked). On a miss it returns KE_UNKNOWN_MODULE (-202), the same as loadcore.

Testing

  • ps2x_tests: all 446 pass, including a new sceCdLayerSearchFile case.
  • ps2xIOP suites: all 4 pass. A new case stages an IRX in IOP RAM only and checks it loads, then checks the module lookup for a hit, a KSEG0 address, a miss and a stopped module. It fails on main.
  • Mana Khemia (SLUS-21735): the game now finds RPK.BIN, all 13 IRX modules load and start (SDRDRV, EzMIDI, CRI ADX…) and it starts GIF DMA. It then stops at a separate problem, a failing sceSifAllocIopHeap in CRI's ps2rna_init_psj, which is out of scope here.

Black frames and frame rate during gameplay

  • Black or half-drawn frames: presentation copied live VRAM while the async GS thread was behind the EE. DISPFB already pointed at the buffer the GS thread was still clearing or drawing, so the window showed black frames and UI missing elements. Now the GS thread captures VRAM and the display registers at each vblank, in stream order. While it is behind, presentation shows that captured frame; once the queue drains, it shows live memory as before. In Mana Khemia gameplay, black flashes went from 181 per 4,800 presented frames to 0 (the 11 that remain are the intro's own fades). New test: GS thread presentation shows the frame finished at the latest vblank it reached.
  • FPS drop in 3D scenes: in field scenes the EE waits on a full GS queue, and the raster threads spend their time in DrawTriangle. The scenes are GS fill-rate bound. The default raster thread count is now all cores but two instead of half the cores. On an 8-core M1 Pro, field gameplay goes from 20 to 27 vblanks/s, and it doesn't improve beyond 6 threads. PS2X_GS_THREADS still overrides it.

Mana Khemia gameplay: SDK stub accuracy, GS rasterizer, recompiler coverage, audio

Thirty-nine more commits, grouped by area:

  • SDK stubs matched to the real libraries (libgraph, libvu0, libdma, newlib): sceGsResetGraph, sceGsSetDefDispEnv/SetDefDBuffDc/SetDefDrawEnv2/SetDefClear, sceGsPutDrawEnv, sceGszbufaddr, the sceVu0* matrix/vector/clip routines, sceDmaSend*, rand/srand, argument 5-7 reads from t0-t2, IRX argc/argv and look_ctype_table. Each commit names the behaviour it corrects; together they fix stretched geometry, wrong clears and the interlaced field-mode presentation.
  • GS rasterizer throughput: batches split into 8-row bands handed out on demand, row-chunked textured triangles and sprites, NEON span blending, compile-time colour/depth formats, framebuffer clears on the raster threads, and the GS thread queuing the next batch while workers draw. PS2X_GS_STATS counts primitives, batches, flush reasons, pixels and GS thread time. Field scenes run at 58-60 vblanks/s on an 8-core M1 Pro.
  • Recompiler function discovery: switch jump-table labels stay inside their function; handler tables laid out at a fixed stride in .data are discovered as functions (Mana Khemia's battle command handlers were merged into a neighbour, so opening Skill in battle dispatched to a missing target and froze); the delay slot of a branch in a function's last word is decoded; and every block start after an unconditional transfer is registered as a resume entry (14,203 in Mana Khemia), so an indirect call into a merged routine lands on its owner instead of leaving the thread dormant. The runtime now reports the first 16 missing targets instead of one.
  • Presentation: the vblank capture is shown whenever one exists for the requested tick, not only when the GS thread is behind. Live VRAM raced the EE clearing the next draw buffer before DISPFB moved, giving 4-21 black frames per 5 s while walking; now 0-4, all around load stalls.
  • Audio: the IOP mixes at 48 kHz of EE-accounted cycles, which drifts between 47.2 and 49.6 kHz against the device clock. The host consumer now resamples toward a 100 ms queue (up to ±5%) instead of skipping or starving; underruns and drops went from +4 to +30 per 5 s to 0.
  • Perf log (PS2X_PERF_LOG): guest vblanks and host frames per second, black frames with the last offending tick and DISPFB/source pointers, audio underruns, dropped frames and resample rate. A vblank that fires more than a period late is re-anchored so a slow stretch is not repaid by running faster than real time.

Testing: ps2x_tests 522 pass. Mana Khemia: title, Load Game, field (The Heights) and battle with the Skill list open, no missing targets, zero audio underruns or drops.

@danfreitas97

Copy link
Copy Markdown

Heads-up from a related case, so the two don't get confused: I hit the same kind of hang in Def Jam: Fight for NY (SLUS-21004), but on a different libcdvd call, and this PR doesn't cover it (nor does my fix cover this one).

There, sceCdReadDvdDualInfo (0x0033CF28, recognized by the analyzer's SCE database but listed under entry_points because the runtime has no handler) binds CDVDFSV's S-command server 0x80000593 and retries forever, same as sceCdLayerSearchFile with 0x80000597 here. I'm about to open a PR adding a sceCdReadDvdDualInfo handler in the same style as your sceCdLayerSearchFile one.

Two notes:

  • It will touch the same places (ps2_call_list.h, CD.cpp/CD.h, the Ghidra export list), so whichever lands second will need a small rebase. I'll take care of mine if this one goes in first.
  • Def Jam also loads modules through _sceSifLoadModuleBuffer, so your IOP-RAM fix in IopEmulator::loadModuleBuffer may matter for it later. I haven't tested that yet.

@AlenDavid
AlenDavid marked this pull request as draft October 31, 2026 01:32
@AlenDavid

Copy link
Copy Markdown
Author

@danfreitas97 Hey! I'm still working on this. I've moved this PR into draft so we don't step on each other foot. Also, feel free to address me in brazilian portuguese, up to your preference. Bora fazer esse projeto acontecer! 🇧🇷

@AlenDavid
AlenDavid marked this pull request as ready for review November 11, 2026 22:37
@AlenDavid

Copy link
Copy Markdown
Author

@danfreitas97 tá pronto pra revisar mano!

@danfreitas97

Copy link
Copy Markdown

@AlenDavid Brabo, patrão! Logo mais eu olho. Nice work!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Boot hangs when CD search and IOP module loading depend on BIOS LOADFILE/CDVDFSV servers

2 participants