Files
psxrecomp/runtime/include
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
..
2026-07-29 03:18:10 -04:00
2026-07-29 03:18:10 -04:00
2026-08-13 14:46:29 -04:00
ok
2026-08-07 22:14:26 -04:00
2026-08-07 16:42:43 -04:00
2026-07-29 03:18:10 -04:00
2026-08-03 05:26:37 -04:00
2026-08-06 20:34:50 -04:00
2026-08-09 22:24:48 -04:00
2026-08-07 18:11:35 -04:00
2026-08-03 05:26:37 -04:00
2026-08-03 05:26:37 -04:00
2026-07-29 03:18:10 -04:00
2026-07-29 03:18:10 -04:00
2026-08-06 00:52:14 -04:00
2026-08-06 00:52:14 -04:00
2026-07-31 17:14:35 -04:00
2026-08-13 17:38:22 -04:00
2026-07-31 17:14:35 -04:00
2026-07-29 03:18:10 -04:00
2026-07-31 17:14:35 -04:00
2026-07-29 03:18:10 -04:00
2026-07-29 03:18:10 -04:00
2026-08-13 10:37:18 -04:00
2026-08-11 18:59:19 -04:00
2026-08-07 18:57:25 -04:00
2026-07-29 03:18:10 -04:00
2026-07-29 03:18:10 -04:00