Commit Graph

1012 Commits

Author SHA1 Message Date
Matthew Stanley c5b8ccd454 Harden frame pacing diagnostics 2026-08-26 22:41:52 -07:00
Matthew Stanley fa4d77069c mods: expose the scanned-out display size to trusted plugins
A plugin that draws its own overlay primitives needs to know where the
right-hand edge of the screen is, and it cannot work that out for itself.
GPUSTAT carries the horizontal-resolution bits, so a plugin can recover
the coarse mode width, but the visible width also depends on the GP1(06h)
X1/X2 range -- and that register is write-only and mirrored nowhere a
plugin can read.

Ape Escape is the worked example. It scans out 384 (368-mode, dot clock 7,
horizontal range 539..3227) while its mode width is 368, and the usual
assumption of 320 is wrong by 64. A HUD row right-aligned to 316 lands 68
pixels short of the edge, which is exactly what the Quick Gadget Select
mod does today.

psx_mod_display_width()/psx_mod_display_height() return what the presenter
uses, straight from gpu_get_display_info(). Zero means the geometry is not
established yet, and the header tells callers to skip drawing rather than
substitute a guess -- an overlay in the wrong place is worse than an
overlay that waits a frame.

mod_runtime_test covers the passthrough with a stubbed GPU, including the
384-vs-368 case that motivates it and the zero case.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-26 19:32:17 -07:00
Matt Stanley e5b31de3e8 Merge pull request #240 from mstan/pr/sio-card-fixes
sio: keep SPU I_STAT pending across a card ACK; preserve the memory-card new-card flag until the first write
2026-08-26 18:53:35 -07:00
Pegasus 7e3d2f9421 fix(sio): preserve memory card new-card flag until write
(cherry picked from commit 97f95f47963ce05f970c448bcae598a9acd090ef)
2026-08-26 21:43:29 +03:00
Alexandros Mandravillis 3592a19e40 fix(sio): keep SPU I_STAT pending across card ACK
The memory-card ACK path cleared I_STAT bit 9 (SPU) before raising the
SIO0 interrupt. That is cross-device interrupt ownership: an SIO event
consumed a pending SPU source, so SPU-IRQ-driven audio (Konami intro,
MGS dialogue streaming) lost its interrupt during card scans. The clear
originated as a netplay digest-parity experiment (probe ACKs landing on
i_stat_before 0x200 vs 0x000) and was never a hardware behavior.

Remove the clear. A card ACK now only raises bit 7; a pending SPU
interrupt stays pending until the guest performs the SPU/INTC
acknowledgement sequence.

Regression: sio_spu_istat_ownership_test drives the card READ preamble
with SPU pending and asserts bit 9 survives every ACK delivery (fails
before this change, passes after). The pre-existing DISABLED
sio_card_protocol_test baseline is unchanged at 147/309 failures.

(cherry picked from commit f14bd43dcb7f97acfe60e22870d9f0c6c201afb5)
2026-08-26 21:43:29 +03:00
Alexandros Mandravillis 2d67bb9a79 cdrom: cancel stale INT2 on new command (PSX-CD-007)
Cancel the previous command's outstanding second-response interrupt when
a new CD command is accepted (psx-spx command flow; DuckStation
BeginCommand semantics). Without this, a Pause(0x09) INT2 survives into
the guest's next Setmode/Setloc/ReadN sequence and fires mid-read as a
stale COMPLETE — LIBDS drivers treat it as a read failure, corrupting
the streamed resource chain.

Cross-validated: C-12 Final Resistance mission 1 PC=0 null-callback
termination disappeared after absorbing this fix (DEVLOG 2026-08-05).
MOHU Mission 1 freeze in a DsRead FATAL/retry loop on single-sector
marker reads.

Also guard beetle SIGSTOP trap behind #ifndef _WIN32 (Windows has no
SIGSTOP).

(cherry picked from commit 2c43fc69c66242a86183156a6f1e60b2f81b1e47)
(cherry picked from commit c03fe5ba3d49241dfaedb344db6ad9c977070722)
2026-08-26 21:43:27 +03:00
Alexandros Mandravillis 2795b5a01a runtime: set READ status after seek complete (PSX-CD-003)
(cherry picked from commit 3e94eccc089550b44abb30dc522cbda7c46713fe)
(cherry picked from commit f9c938c36075bf145af26ba9891d5e239c16d4e0)
2026-08-26 21:43:27 +03:00
Matthew Stanley cd68169cb8 savestate: reject dirty-pump snapshot contexts 2026-08-25 23:49:59 -07:00
Matthew Stanley 432a44f64a input: add plugin controller presentation policies
Supersedes the global HYBRID controller-mode shape from PR #233 with a trusted plugin callback that can choose analog or digital presentation per input sample.

Keeps analog mode hardware-faithful by no longer folding physical D-pad input onto the left stick, while leaving debug injection able to steer stick-only paths.

Includes the pad/cursor probe tooling from #233 for diagnosing input doubling.
2026-08-25 20:37:49 -07:00
Matt Stanley 20ae6db9b4 Merge pull request #232 from mstan/split/pr193-keyboard-pad-chord-test
tests: cover keyboard Select+Start pad chord
2026-08-25 14:23:53 -07:00
Matt Stanley d5b5315ea4 Merge pull request #231 from mstan/split/pr193-sdl3-audio-stream-spec
audio: report SDL3 stream input spec to runtime
2026-08-25 14:23:49 -07:00
Matt Stanley 707f839570 Merge pull request #230 from mstan/split/pr193-savestate-no-pc0
savestate: defer saves until a safe resume PC exists
2026-08-25 14:23:46 -07:00
Matthew Stanley 19e5de4c28 boot_state: document unknown-section skip 2026-08-25 13:45:11 -07:00
Matthew Stanley ea0530eef0 tests: cover keyboard Select+Start pad chord
Split from parked PR #193. Adds the standalone keyboard_pad_chord_test target and verifies that default keyboard bindings emit Select, Start, and Select+Start without one key masking the other.
2026-08-25 13:10:52 -07:00
Matthew Stanley 151588d62e audio: report SDL3 stream input spec to runtime
Split from parked PR #193 commit 42109c58. SDL3 audio streams convert from the requested stream format to the device format internally, so psx_sdl_audio_open must report the requested stream input spec back to the runtime. Reporting the device format made the runtime generate at the wrong rate before SDL resampled again.
2026-08-25 13:07:40 -07:00
tetrisgm b0424b0b4e savestate: defer saves until a safe resume PC exists
Split from parked PR #193 commit 1469550c. The mod-state preservation half is already covered on master by the framework mod staging cleanup, so this extraction keeps only the independent save-state fix: when the resume hint is 0, wait for a published block-leader PC instead of writing a state that would later resume at pc=0.
2026-08-25 13:05:23 -07:00
tetrisgm 1d32acf74f boot_state: skip unknown sections instead of failing the whole load
`default: return 0` made every savestate written by a build with one extra
section a poison pill for every build without it. Worse than unloadable: by the
time the apply loop hit the unknown tag it had already restored RAM, VRAM and
CPU state, so the refusal left a HALF-RESTORED machine that wedged or died at
PC=0 -- which presented as "loading a save crashed the whole thing", with the
restored-but-untracked VRAM showing low-res in the wreckage.

A sectioned state format exists precisely so readers can step over what they do
not know. Known-but-malformed sections still fail as before; only genuinely
unknown tags are skipped.

This also makes the states saved during the short-lived BS_SEC_TEXPACK window
load cleanly again (the extra section is now ignored), and it is the
forward-compatibility prerequisite for ever adding that section back.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 13:03:17 -07:00
Matt Stanley 8943695aab Merge pull request #217 from mstan/codex/hd-texture-pack-core
runtime: add external HD texture pack core
2026-08-25 12:46:36 -07:00
Matt Stanley f294f7397a Merge pull request #226 from mstan/hotfix/linux-selfcompile
Self-compile hotfixes: offline dependency vendoring, GLVND GL, Windows/MinGW C++17, and a green ctest
2026-08-25 11:59:06 -07:00
Matt Stanley c3d7fc5f71 Merge pull request #216 from mstan/codex/game-window-width
config: honor title window width
2026-08-25 11:51:40 -07:00
Matt Stanley 33be1ac338 Merge pull request #225 from mstan/fix/overlay-region-high-loading-exe
Treat RAM below a high-loading boot EXE as overlay region
2026-08-25 11:46:16 -07:00
Matthew Stanley d66b8e75fd tests: cover high-load overlay region predicate 2026-08-25 11:36:34 -07:00
Matt Stanley 6c65801caa Merge pull request #222 from mstan/codex/text-xlate-fix-only-tables
text: load fix-only translation tables
2026-08-25 11:31:40 -07:00
Matt Stanley 3612b91066 Merge pull request #215 from mstan/codex/keymap-event-scancode
input: match host hotkeys by event scancode
2026-08-25 11:31:11 -07:00
Matt Stanley 3c976fdb85 Merge pull request #221 from mstan/codex/overlay-slti-lower-shim
autocompile: define lower-cull helper in overlay shim
2026-08-25 10:19:42 -07:00
Matt Stanley 45ceb81174 Merge pull request #214 from mstan/codex/screenshot-hires-byte-stride
runtime: fix high-resolution screenshot byte stride
2026-08-25 10:19:17 -07:00
Matt Stanley d41cf53c6d Merge pull request #219 from mstan/codex/main-ram-geometry
memory: define retail and expanded RAM geometry
2026-08-25 10:01:41 -07:00
Alex Vanderveen 7efc712dd3 Windows/MinGW self-build: pin cxx_std_17 and stop capturing a structured binding
Two defects that only bite a MinGW C++17 build, both still live on master.

runtime.cmake declared only `c_std_11` on the runtime target, leaving the C++
dialect to whatever the parent project happened to set. A game CMakeLists that
omits CMAKE_CXX_STANDARD therefore compiled mod_packages.cpp as a pre-17
dialect. State the runtime's real requirement instead of inheriting it.

mod_packages.cpp captured a structured binding (`for (const auto& [id, ...])`
with `id` used inside a lambda). That is a C++20 extension; clang accepts it
silently, MinGW/GCC at C++17 rejects it. Bind the map entry to ordinary locals
before the lambda.

Cherry-picked from ae9432a3 on feat/rbengine-bak, minus that commit's unrelated
lib/recomp-net submodule bump. Verified by building psx-runtime on Linux.
2026-08-25 12:24:05 -04:00
Alex Vanderveen 2bea610f87 Treat RAM below a high-loading boot EXE as overlay region
The overlay-region model assumed the boot EXE sits at the bottom of RAM:
[KERNEL_END, text_end) is main-EXE text, [text_end, RAM) is runtime-loaded
overlay code. That holds for a load_address of 0x80010000, where the text
base equals the kernel-window end and the region below the text is empty.

It is false for a boot EXE that loads HIGH and streams its gameplay code
into the RAM beneath itself. Klonoa loads a 46 KB stub at 0x80180000 and
runs its overlays from 0x10000-0x130000, so the floor landed at 0x18B000
and the entire overlay region fell below it:

  - dirty_ram_clear_image_baseline() wiped the dirty bits across
    [0x10000, 0x18B000) believing it was the clean compiled text image.
  - the dirty_ram_dispatch_inner admit heuristic requires phys >= floor,
    so a JALR into an overlay page the CD had DMA'd there (never touched
    by the CPU write hooks, so never marked dirty) was refused by the
    interpreter, fell through to psx_unknown_dispatch and fail-fast
    exit(1) — Klonoa, frame 687, target 0x80123D00.

Pin the text BASE (g_text_image_lo) alongside the floor and route the
gates through one predicate: the overlay region is both sides of the text
image, not just above it. Street Fighter Alpha 3 (0x80113B00) and
Bomberman Fantasy Race (0x8003004C) load high too.

No-op for bottom-loading games: g_text_image_lo stays at its 0x10000
default, the below-text clause is empty, and the baseline clear starts at
the same page as before.

Klonoa now runs 6874+ frames at a locked 60 fps with fail-fast on and
zero unknown dispatches; it previously died at frame 687.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
(cherry picked from commit cf742f3190)
(cherry picked from commit 30e8a71f93539c4e10ff6767c44c24137c9dd714)
(cherry picked from commit 94f184f744)
2026-08-25 11:17:51 -04:00
Alex Vanderveen c49c8ec9f5 tests: fix three Linux ctest failures (stale assertions + a lock-path mismatch)
Reported in mstan/psxrecomp#224. All three are test-only defects; no product
code is at fault.

test_launcher_vulkan_option: `gi` became a pointer and the surrounding code was
reformatted, so two assertions no longer match the source. The issue reports one
because Python `assert` stops at the first failure; line 25 fails identically and
also renamed `vulkan_offered` -> `vulkan_offered_b`. Blame is 61279a9d ("bios
select and rematch pathing fix", 2026-08-10), not 9228f32c -- 9228f32c ADDED
both the source lines and the assertions as a matching pair.

test_vk_present_wait_stage: not a stale assertion. `652be7ba` added a forward
declaration of submit_present above the definition, and the regex
`static void submit_present\(.*?\n\}` latches onto the prototype, so `body`
became the *next* function (finish_present) and submit_present was never
examined. VK_PIPELINE_STAGE_TRANSFER_BIT passed coincidentally on
finish_present's barrier -- a false pass. Anchor the match on the definition by
requiring `)` followed by `{`.

test_aot_overlay_discovery (check_atomic_dll_publication): the test hardcodes
".dll" shard names, but load_region_coverage re-derives the shard path via
overlay_ext(), which is ".so" on Linux. The writer held
<stem>.dll.pair-lock while the coverage reader took <stem>.so.pair-lock --
different files, no contention, so the reader crossed the publication window in
0.000s. This is why only one of the two readers failed: shard_pair_files_complete
is handed the .dll path directly and locked correctly. The pair-lock machinery in
compile_overlays.py is sound (fcntl.flock; all five renames in the expected
order); only the test's naming was wrong. Derive the extension instead.

Verified on Linux: tests 1 and 2 pass; check_atomic_dll_publication now blocks
both readers (1.004s / 1.016s) and passes in 2.4s.

NOT fixed here: aot_overlay_discovery still fails later, in
check_candidate_capacity_publication, from the SAME class of defect -- 80
hardcoded ".dll" literals remain in that file and cache_candidate_inventory also
filters on overlay_ext(). That is a wider Linux port of the test than #224
describes and needs an owner. Note also that recompiler/CMakeLists.txt:485 claims
this test is DISABLED, but no set_tests_properties marks it so; only the three
known-failing guards are actually disabled.

(cherry picked from commit 25555579dc)
2026-08-25 11:17:44 -04:00
Alex Vanderveen db2c03e109 hotfix for offline build deps
(cherry picked from commit 986de03fcf)
2026-08-25 11:17:44 -04:00
Alex Vanderveen e0037d7433 cmake patch hardening for steamos
(cherry picked from commit 8e0cad4685)
2026-08-25 11:17:44 -04:00
Codex 7972b536fb text: load fix-only translation tables 2026-08-25 02:11:25 -07:00
Codex 01e17cbf6e autocompile: define lower-cull helper in overlay shim 2026-08-25 02:05:49 -07:00
Codex 2346bd0590 memory: define retail and expanded RAM geometry 2026-08-25 01:57:44 -07:00
Codex 4591c49958 timing: add rational VBlank period generator 2026-08-25 01:54:42 -07:00
Codex 4af774e913 style: normalize HD texture source endings 2026-08-25 01:50:55 -07:00
Codex 98aaea953b runtime: add external HD texture pack core 2026-08-25 01:50:23 -07:00
Codex f64ca0ce3c config: honor title window width 2026-08-25 01:45:55 -07:00
Codex 52861da621 input: match host hotkeys by event scancode 2026-08-25 01:43:25 -07:00
Codex 1d4a659ba7 runtime: fix high-resolution screenshot byte stride 2026-08-25 01:39:33 -07:00
Matt Stanley 1ea6a45066 Merge pull request #187 from mstan/split/pr169-load-accel-audio-sink
runtime: sink audio during accelerated loads
2026-08-24 22:43:41 -07:00
Matt Stanley 979e5e330c Merge pull request #204 from mstan/split/pr169-fmv-depth24-mirror-sync
gl: sync CPU mirror on 24-bit FMV entry
2026-08-24 22:27:23 -07:00
Matt Stanley 15e5eb3ead Merge pull request #205 from mstan/split/pr169-fmv-filter-user-setting-clean
presentation: add opt-in FMV filter setting
2026-08-24 20:29:11 -07:00
Matthew Stanley 15099cc5b6 presentation: add opt-in FMV filter setting 2026-08-24 20:28:43 -07:00
Alex Vanderveen ffa11fa2dc cdrom: a Read that continues the current stream must not restart the drive
Legend of Mana's land-creation effect rendered as hard-edged coloured
wedges instead of a soft bloom. The cause was in the CD-ROM controller,
not the renderer.

The game streams a file by issuing Setloc(next)+ReadN roughly 10,800
guest cycles after each sector arrives. Every ReadN called
start_read_stream(), which clears the sector buffer -- destroying the
sector that had already been delivered and announced but not yet drained
by the guest. Measured at the palette load:

  buffer      psx-runtime   DuckStation
  0x0E1F18      125111        125111
  0x0E2718      125113        125112   <- the effect's colour palette
  0x0DCF18      125113        125113

Sector 125112 carries the effect's 5-colour palette (0x0888F8 =
(248,136,8), 0xB0F8F8 = (248,248,176), verified against the disc image).
Destroyed before the guest could take it, its buffer received 125113's
raw file bytes instead, and the colour routine faithfully scaled those
into 216 distinct vertex colours -- the wedges. The table now holds 5.

DuckStation only calls BeginReading -- the function that clears the
buffers and re-arms the read-start latency -- when the request actually
moves the drive:

  if ((!setloc_pending || setloc_position.ToLBA() == GetNextSectorToBeRead())
      && (drive_state == Reading || ...))
    "Ignoring read command with pending/same setloc, already reading"
  else
    BeginReading();

A Read issued while the drive already streams the sector the pending
Setloc names is the game saying "keep going", not "start over". The
command is still ACKed with the same INT3; only the restart is skipped.

Two things fall out of it. The un-drained sector survives, so the game
receives the consecutive sectors it expects (109,110,111,112,113 rather
than 109,111,111,113,113). And each request stops paying
initial_read_delay_cycles() again: inter-sector gaps drop from ~456,000
cycles to 225,792 -- exactly the 2x cadence -- so this load takes 2 guest
frames instead of 13, matching DuckStation. int1_pended goes 10 -> 0.

Also included, developed alongside and verified in the same runs:

  - An 8-slot sector buffer ring, as the hardware has (nocash;
    DuckStation NUM_SECTOR_BUFFERS = 8). A delivered sector fills the
    NEXT slot and the INT1 that announces it latches the read slot, so a
    sector arriving mid-drain no longer clobbers the FIFO. The read
    pointer follows the slot each INT1 announces, captured when the INT1
    is RAISED: this drive free-runs, so latching "newest" at
    presentation time handed the guest a sector seven ahead of the one
    it asked for.
  - The seamless no-IRQ refill advances the read pointer, because it is
    a continuation of the drain already in progress. Without it a
    multi-sector DMA starves mid-transfer (measured: 1,128,960 starved
    drains).
  - A pended data-ready is released on a short schedule rather than
    inside the guest's ack store, matching DuckStation's
    QueueDeliverAsyncInterrupt.
  - Flow control for accelerated (non-1x) reads: never overwrite a
    sector the guest has not consumed while running faster than
    hardware, capped at the authentic sector period. Inert at authentic
    timing.

New cdrom_state counters ring_starved and ring_dropped, plus
cdrom_timing_dump for per-sector deadline/delivery records, are the
regression tripwires: ring_starved and int1_lost must stay 0.

Verified on Legend of Mana (SLUS-01013): the effect renders correctly,
the palette table holds 5 colour words, sectors arrive at the 2x
cadence, and no notification is lost.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-24 20:05:08 -07:00
Matt Stanley d1df20514f Merge pull request #206 from mstan/fix/pr199-headless-no-recomp-ui-setup-host
runtime: fix headless generated-game builds without recomp-ui
2026-08-24 17:26:39 -07:00
Matthew Stanley 69f851f0f3 runtime: omit setup host sources without recomp-ui 2026-08-24 17:25:24 -07:00
Matthew Stanley 1f5f70579e gl: sync CPU mirror on 24-bit FMV entry 2026-08-24 17:12:27 -07:00
Matthew Stanley 84a901289d mods: make bezel artwork user-selected 2026-08-24 17:03:52 -07:00