launcher: stb-based launcher revamp (parked — needs Phase-2b GL loader) - #13
launcher: stb-based launcher revamp (parked — needs Phase-2b GL loader)#13mstan wants to merge 16 commits into
Conversation
Salvaged from PR #13 (feat/launcher-stb-revamp, commits 2ba52e9/a40d587b) by NyperYuhgard: - COP2 condition branches (BC2F/BC2T) recognized end-to-end: decoder mnemonics, control-flow analyzer (is_control_flow/analyze_instruction), and branch-condition codegen. Previously these fell through to the 'unknown branch: never taken' default. - MOVN/MOVZ (SPECIAL 0x0A/0x0B): decoded, classified, and translated. Previously silently no-op'd as TODO comments. - Branch-likely (0x14-0x17) and REGIMM-likely decoder mnemonics. Corrections to the original: - BC2F/BC2T encoding: both are rs=0x08 with rt bit 0 selecting T/F; the original treated rs=0x09 as BC2T (undefined encoding). - BC2 condition: the original tested invented GTE FLAG bits via gte_ctrl[31]. The PS1 GTE does not drive the COP2 condition input (psx-spx), so BC2F emits always-taken and BC2T never-taken. - MOVZ emitted the same condition as MOVN; corrected to rt == 0. Dropped from the original: - The function_analysis.cpp DISCOVERY validity-table widening (accepting MOVN/MOVZ, branch-likely, REGIMM-likely rt values, addi prologues as valid MIPS words). These opcodes raise Reserved Instruction on a real R3000A, so no genuine PS1 code contains them; accepting them only admits DATA into discovery — measured on Tomba 1 (SCUS-94236): +9 spurious dispatch entries, all data-as-code (movn/movz writing $zero). With this dropped, generated output is byte-identical to master for both SCPH1001 and Tomba 1 — the codegen additions are reachable only via seed-forced regions, as belt-and-braces over the TODO no-op. - Enumerated REGIMM branch-likely condition cases (master's every-rt hardware decode already covers them) and the post-delay-slot branch-and-link write (master already links all four regimm link forms before the delay slot). Co-Authored-By: NyperYuhgard <noreply@github.com> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…nert) Salvaged from PR #13 (feat/launcher-stb-revamp, commits 2ba52e9/a40d587b) by NyperYuhgard. What remains after measurement (see below): - generate_branch_condition: BC2F/BC2T (opcode 0x12, rs=0x08, rt bit 0 selects T/F). The PS1 GTE does not drive the COP2 condition input (psx-spx), so BC2F emits always-taken, BC2T never-taken. - translate_instruction: MOVN/MOVZ (SPECIAL 0x0A/0x0B) translated instead of silently no-op'd as TODO comments; COP2 BC sub-op emits an explicit no-op marker in-block. - mips_decoder: mnemonics/classification for MOVN/MOVZ, BC2F/BC2T, REGIMM-likely and branch-likely forms (diagnostic output only). Corrections to the original: BC2T was mis-encoded as rs=0x09 (both are rs=0x08); the BC2 condition tested invented GTE FLAG bits; MOVZ emitted MOVN's condition (corrected to rt == 0). Dropped from the original, with measurement: - function_analysis validity-table widening and control_flow BC2 branch classification. MOVN/MOVZ/branch-likely raise Reserved Instruction on a real R3000A, and no PS1 compiler emits them, so widening discovery only admits DATA: measured +9 spurious dispatch entries on Tomba 1 (SCUS-94236), all data-as-code (movn writing $zero), each one replacing a fail-closed psx_unknown_dispatch with compiled garbage. - REGIMM 0x12/0x13 branch-and-link: R3000A links iff (rt&0x1E)==0x10, so rt 0x12/0x13 must NOT link; the original emitted phantom $ra writes in swept data regions. With those dropped, generated output is byte-identical to master for SCPH1001, OpenBIOS, and Tomba 1 — no cg-hash roll, no reshard. The kept codegen is reachable only for seed-forced or future-discovered regions, as belt-and-braces over the TODO no-op. Co-Authored-By: NyperYuhgard <noreply@github.com> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…dd CFC0 Salvaged from PR #13 (feat/launcher-stb-revamp, commit 9fd2876) by NyperYuhgard. Adds to the dirty-RAM interpreter: - LoadAddressError/StoreAddressError delivery for misaligned LH/LW/LHU/ SH/SW and misaligned JR/JALR targets, via a new interp_exception() that performs the architectural COP0 sequence (BadVAddr, Cause ExcCode, SR mode-stack push, EPC, BEV-selected vector). - COP0 Cause write protection on MTC0/CTC0. - CFC0/CTC0 aliases (identical to MFC0/MTC0 on R3000A). Two deviations from the original: - The original masked Cause writes to bits [15:10] (backwards vs hardware); corrected to the writable software-interrupt bits [9:8] per psx-spx / PCSX-Redux, which also un-deadens the psxTestSWInts recheck below it. - The original's post-RFE immediate psx_check_interrupts recheck is dropped: master already delivers post-RFE interrupts through psx_rfe_mark_escape() and re-checking inside the handler window is the documented dbg-build boot-wedge livelock. Co-Authored-By: NyperYuhgard <noreply@github.com> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Salvaged from PR #13 (feat/launcher-stb-revamp, commit 2ba52e9) by NyperYuhgard. Master fatal-halts on any DMA start for channel 5; this moves words to/from RAM directly (device side unemulated: RAM-to-device reads-and-discards, device-to-RAM zero-fills) and completes the transfer with the normal IRQ path. Note: zero-filling from an unemulated expansion device is weaker than the fail-closed psx_fatal_halt default the other channels follow; kept as submitted because the contributor exercised it on a real title. Trimmed from the original hunk: unused DMA_PIO_CYCLES_PER_WORD macro and mdec_finish_async_{in,out}_transfer wrappers nothing references. Co-Authored-By: NyperYuhgard <noreply@github.com> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Salvaged from PR #13 (feat/launcher-stb-revamp, commit 2ba52e9) by NyperYuhgard. Fills the gaps the file header listed as not modeled: - PS1-style reverb delay lines processing the final mix - noise generator (LFSR) with per-voice noise mode and the noise-level register pacing steps - per-voice sweep volume envelopes (volume reg bit 15) alongside the existing direct mode - pitch modulation (PMON) of a voice's step by the previous voice - SPU IRQ (i_stat bit 9) when the ADPCM decoder crosses the IRQ address Merged around master's later work: Gaussian interpolation stays the sample source (noise replaces it only in noise mode), and the voice loop keeps master's any_voice gate, voice-sum tap, and shadow-tap instrumentation with the branch's sweep-volume selection swapped in. Co-Authored-By: NyperYuhgard <noreply@github.com> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…dd CFC0 Salvaged from PR #13 (feat/launcher-stb-revamp, commit 9fd2876) by NyperYuhgard. Adds to the dirty-RAM interpreter: - LoadAddressError/StoreAddressError delivery for misaligned LH/LW/LHU/ SH/SW and misaligned JR/JALR targets, via a new interp_exception() that performs the architectural COP0 sequence (BadVAddr, Cause ExcCode, SR mode-stack push, EPC, BEV-selected vector). - COP0 Cause write protection on MTC0/CTC0. - CFC0/CTC0 aliases (identical to MFC0/MTC0 on R3000A). Two deviations from the original: - The original masked Cause writes to bits [15:10] (backwards vs hardware); corrected to the writable software-interrupt bits [9:8] per psx-spx / PCSX-Redux, which also un-deadens the psxTestSWInts recheck below it. - The original's post-RFE immediate psx_check_interrupts recheck is dropped: master already delivers post-RFE interrupts through psx_rfe_mark_escape() and re-checking inside the handler window is the documented dbg-build boot-wedge livelock. Co-Authored-By: NyperYuhgard <noreply@github.com> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Salvaged from PR #13 (feat/launcher-stb-revamp, commit 2ba52e9) by NyperYuhgard. Master fatal-halts on any DMA start for channel 5; this moves words to/from RAM directly (device side unemulated: RAM-to-device reads-and-discards, device-to-RAM zero-fills) and completes the transfer with the normal IRQ path. Note: zero-filling from an unemulated expansion device is weaker than the fail-closed psx_fatal_halt default the other channels follow; kept as submitted because the contributor exercised it on a real title. Trimmed from the original hunk: unused DMA_PIO_CYCLES_PER_WORD macro and mdec_finish_async_{in,out}_transfer wrappers nothing references. Co-Authored-By: NyperYuhgard <noreply@github.com> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Salvaged from PR #13 (feat/launcher-stb-revamp, commit 2ba52e9) by NyperYuhgard. Fills the gaps the file header listed as not modeled: - PS1-style reverb delay lines processing the final mix - noise generator (LFSR) with per-voice noise mode and the noise-level register pacing steps - per-voice sweep volume envelopes (volume reg bit 15) alongside the existing direct mode - pitch modulation (PMON) of a voice's step by the previous voice - SPU IRQ (i_stat bit 9) when the ADPCM decoder crosses the IRQ address Merged around master's later work: Gaussian interpolation stays the sample source (noise replaces it only in noise mode), and the voice loop keeps master's any_voice gate, voice-sum tap, and shadow-tap instrumentation with the branch's sweep-volume selection swapped in. Co-Authored-By: NyperYuhgard <noreply@github.com> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…dd CFC0 Salvaged from PR #13 (feat/launcher-stb-revamp, commit 9fd2876) by NyperYuhgard. Adds to the dirty-RAM interpreter: - LoadAddressError/StoreAddressError delivery for misaligned LH/LW/LHU/ SH/SW and misaligned JR/JALR targets, via a new interp_exception() that performs the architectural COP0 sequence (BadVAddr, Cause ExcCode, SR mode-stack push, EPC, BEV-selected vector). - COP0 Cause write protection on MTC0/CTC0. - CFC0/CTC0 aliases (identical to MFC0/MTC0 on R3000A). Two deviations from the original: - The original masked Cause writes to bits [15:10] (backwards vs hardware); corrected to the writable software-interrupt bits [9:8] per psx-spx / PCSX-Redux, which also un-deadens the psxTestSWInts recheck below it. - The original's post-RFE immediate psx_check_interrupts recheck is dropped: master already delivers post-RFE interrupts through psx_rfe_mark_escape() and re-checking inside the handler window is the documented dbg-build boot-wedge livelock. Co-Authored-By: NyperYuhgard <noreply@github.com> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Salvaged from PR #13 (feat/launcher-stb-revamp, commit 2ba52e9) by NyperYuhgard. Master fatal-halts on any DMA start for channel 5; this moves words to/from RAM directly (device side unemulated: RAM-to-device reads-and-discards, device-to-RAM zero-fills) and completes the transfer with the normal IRQ path. Note: zero-filling from an unemulated expansion device is weaker than the fail-closed psx_fatal_halt default the other channels follow; kept as submitted because the contributor exercised it on a real title. Trimmed from the original hunk: unused DMA_PIO_CYCLES_PER_WORD macro and mdec_finish_async_{in,out}_transfer wrappers nothing references. Co-Authored-By: NyperYuhgard <noreply@github.com> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Closing this out — everything worth keeping has landed on master, credited to @NyperYuhgard. Merged from this PRHarvested earlier (July), each with attribution:
Merged today, after validating on Tomba 1, Tomba 2, Ape Escape and Mega Man X6:
Three bugs in those two were fixed in-port rather than carried forward: the COP0 Cause write mask was inverted (it made the read-only hardware-IP bits writable and locked out the writable software-interrupt bits, which also dead-coded its own SW-interrupt recheck), the post-RFE interrupt recheck collided with master's Not merged, with reasonsSPU reverb / noise / sweep / PMON. Rejected. The gap is real — master still says reverb is not modelled — but this implementation's parameter mapping is invented: it reads Recompiler changes (BC2F/BC2T, MOVN/MOVZ, branch-likely). Dropped after measurement. MOVN/MOVZ and the branch-likely forms raise Reserved Instruction on a real R3000A, so widening the discovery validity tables only admits data-as-code: measured on Tomba 1, it added 9 spurious dispatch entries, every one of them data ( The stb launcher. Obsolete — the engine no longer has its own launcher UI; the shared recomp-ui/ImGui launcher replaced it, and the RmlUi remnants were stripped in #76. Also came out of thisThe |
Extract the useful diagnostic from PR mstan#13 into an opt-in, title-agnostic boot write-range trace with TCP-queryable context. Co-authored-by: NyperYuhgard <93950153+NyperYuhgard@users.noreply.github.com>
…inational Closes the four gaps named in issue mstan#103 and lands the separable, verified parts of PR mstan#102. See docs/internal/SPU_FIDELITY_103.md for the per-piece disposition and the oracle-verification queue. SPU DSP (issue mstan#103), clean-room from hardware documentation: - Reverb: the documented 22050 Hz engine — vLIN/vRIN input, same-side and crossed different-side IIR reflections, 4-tap comb early echo, both all-pass filters, vLOUT/vROUT output, and the work-area address advance. Gated on SPUCNT bit 7 for WRITES only; reads, IRQ checks, address advance and output always run, as on hardware. Addressing is confined to [mBASE, 0x80000). Per-voice sends from EON, CD send from SPUCNT bits 0+2. - Noise: 16-bit LFSR with the documented parity feedback, clocked at the SPUCNT bits 8-13 rate; NON voices substitute it for their ADPCM sample. - Volume sweeps: all 24 voice L/R plus main L/R. Bit 15 selects sweep vs direct; sweep is a live envelope on the same rate machinery as ADSR, and a guest read of a sweeping register returns the live level. - Capture buffers: CD L/R and voice 1/3 output written to SPU RAM, so a game that parks the IRQ address there still gets its interrupt. - SPU IRQ (I_STAT bit 9): checked at every SPU RAM access class — FIFO, DMA both directions, ADPCM block fetch, capture writes, reverb work-area accesses — gated on SPUCNT bit 6, mirrored in SPUSTAT bit 6, acked by clearing SPUCNT bit 6. The CD-only fast path in spu_render was removed rather than extended: the SPU now has per-frame work that must run in every path, and an FMV (XA audio with zero active voices) is exactly the CD-with-reverb case. This is a clean-room implementation from the psx-spx/nocash register map and documented algorithm. Beetle's source was deliberately not consulted: it is GPL-2.0-or-later and this project is PolyForm Noncommercial. PR mstan#16 was parked for exactly that reason and PR mstan#13's reverb was separately wrong (it gated on bit 15 of dAPF1, an address offset, instead of SPUCNT bit 7). The one place this knowingly deviates from hardware is the 22.05 -> 44.1 kHz reconstruction filter, which the documentation does not specify; it is isolated in a single function and flagged for oracle comparison. CAUSE.IP2 (from PR mstan#102): IP2 is combinational on R3000A — it mirrors the interrupt line and falls the instant the guest acks I_STAT or masks the source. It was being OR'd in at delivery and never cleared, in TWO places (the compiled path and psx_interpreter.c, the latter missed by PR mstan#102), leaving a phantom pending interrupt that can spin a kernel dispatcher looping on CAUSE.IP & SR.IM. psx_irq_refresh_cause_ip2() is now the sole writer of bit 10, refreshed at raise, I_STAT ack, I_MASK write, HLE context restore and power-on. Verified against the Beetle oracle's irq.cpp/cpu.cpp rather than asserted, and covered by test_cause_ip2_combinational. Mid-dispatch audio pump (from PR mstan#102), with a fix: routed through a gate that mirrors sdl_audio_update's mute/sink decision. Pumping unconditionally from the VBlank edge, as submitted, would have pushed real audio during every turbo-load hard mute and defeated the freeze-in-place mute model. SPU RAM DMA readback (from PR mstan#102): DMA4 in the SPU->RAM direction wrote literal zeros; SPU RAM is readable memory and titles carry state through it across an Exec boundary. Also fixes an unrelated build race found on the way: tools/embed_spirv.py wrote its intermediate SPIR-V to <source>.spv, so the two runtime targets that embed the same shaders raced on one path and one deleted the file the other was reading. Co-Authored-By: Alexandros Mandravillis <Alexbeav@live.com> Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
What
Replaces the RmlUi launcher with a self-contained stb-based UI (stb_truetype rendering, no RmlUi dependency). Authored by @NyperYuhgard — the three commits here are their original work, cherry-picked onto current master to preserve it as a reviewable branch after master was reverted to RmlUi.
Why parked
Two blockers before this can land (see revert f36b458 on master):
SDL_GL_GetProcAddressloader, so launcher-ON game builds fail to link on MinGW.Master stays on the RmlUi launcher (the configuration every recent release shipped with) until both are addressed. This PR keeps the revamp alive and reviewable instead of buried in pre-revert history.
Credits
All launcher code in this PR: @NyperYuhgard (original author, preserved on the commits).
🤖 Generated with Claude Code