Expand description
Interrupt-driven CD-ROM sector streaming.
The SDK’s SectorReader reads a disc by waiting: fine at boot, wrong in
the middle of a game, where the drive delivers a sector every 6.5 ms and a
frame loop that looks once per frame loses most of them. This crate reads
the way a streamer has to: you queue Requests, the CD interrupt
handler pops one sector per interrupt straight into the request’s
destination, and the foreground only submits, cancels and asks how far a
request has got. Nothing busy-waits for the drive.
§What it does
- PIO, one sector per interrupt. The sector is popped from the data FIFO by the CPU. Chopped CD DMA (channel 3) is unreliable on a console; the PIO path is the one that has loaded byte-perfect streams there. The price is CPU time while a read runs (about 1.3 ms per sector more than a working DMA is the figure on record), so read in bursts when there is work, not continuously.
- Seek first. Every transfer is Setloc, SeekL, Setmode (double speed), ReadN: the bracket the BIOS uses. A bare Setloc plus ReadN starts delivering while the mechanism is still settling and corrupts a long stream on a console.
- Abort at the next sector.
Engine::cancelonly sets a flag; the handler drops the next sector and pauses the drive itself. (A Pause sent from the foreground while sectors were arriving lost its acknowledge on silicon.) The request endsOutcome::Cancelledwith the sectors that had landed;Request::remaining_afterbuilds the request that continues it. - A small priority queue.
QUEUE_DEPTHrequests wait behind the active one, most urgentPriorityfirst. - Contiguous chaining. A queued request that starts at the sector after the active one’s last continues without a Pause or a seek: the handler swaps the destination and keeps popping. A layout-ordered group of regions costs one seek.
- Resume. A request can carry a resume budget
(
Request::with_resumes): after a drive error the transport pauses, seeks back tolba + receivedand carries on. - A drive arbiter. CD-DA and XA playback and data reads cannot share
the laser.
Engine::request_audio_leasestops the read in flight, waits for the drive to stop, closes the CD interrupt source and (on the console) hands the controller token to the audio code; releasing the lease takes it back and queued reads carry on. End audio with Pause, never Stop: after Stop the motor spins down, reports status 0 for a second or two, and reads started then failed on a console. A lease that is asked for and left pending is granted the moment the drive stops, ahead of any read queued meanwhile. Audio that must not hold up reads usesEngine::try_audio_lease, which takes the drive only if it is free and leaves nothing pending, or ends the pending request withEngine::withdraw_audio_lease.
§Use
// After the polled boot reads are done (SectorReader::release gives the
// token back):
psx_cdstream::install(cd, psx_cdstream::Config::DEFAULT).ok();
// SAFETY: ROOM is not touched until the request has finished.
let request = unsafe { Request::new_raw(lba, sectors, ROOM.as_mut_ptr().cast()) };
let ticket = psx_cdstream::submit(request.with_priority(Priority::URGENT))?;
// Each frame:
if let RequestState::Finished(done) = psx_cdstream::state(ticket) {
// done.outcome, done.received
}§How it is built
Engine is the whole state machine, generic over CdHw, the
controller as it sees it. On the console install puts an exception
wrapper in front of psx-rt’s handler that calls the one global engine
once per CD interrupt, and the free functions (submit, state, …)
reach it with the CD source closed. On the host the tests drive the same
engine against a scripted drive, so every phase, cancel point, error path,
chaining rule and lease transition is exercised without hardware. The
counters are published as PSX_CD_STATS (StreamStats) for tools that
read guest memory.
Boot-time and blocking loads still use psx_io::cd::reader::SectorReader
(or psx_pack::cd): it needs the controller token, and so does
install, so the compiler keeps the two from sharing the drive.
uninstall hands the token back.
Structs§
- Completion
- A finished request.
- Config
- How the engine behaves.
- Engine
- The CD transport.
- Failure
- A transfer failure, as the code the transport records for it.
- Mmio
- The CD-ROM controller, through the SDK’s register steps.
- Priority
- How urgent a request is. Lower numbers are served first.
- Request
- A run of sectors to read into memory.
- Stream
Stats - Counters, published as one block (
PSX_CD_STATS) for tools that read guest memory. All wrap at 2^32. - Ticket
- Names a submitted request.
Enums§
- Failure
Kind - What a
Failurecode says. - IrqMask
Policy - Whether the CD interrupt source stays open between transfers.
- Lease
State - Where an audio lease request stands.
- Outcome
- How a request ended.
- Owner
- Who may program the controller.
- Phase
- Where the controller is in a transfer. The numbers are published in
PSX_CD_STATSand match the earlier game-local transports. - Request
State - Where a request is in its life.
- Submit
Error - Why a request was not accepted.
Constants§
- HANDLER_
STACK_ BYTES - Bytes of private stack the handler runs on.
- QUEUE_
DEPTH - Requests that can wait behind the active one.
- RECENT_
RESULTS - Finished requests whose results
Engine::statestill remembers. - SECTOR_
BYTES - Bytes in one data sector.
- SECTOR_
WORDS - Words in one data sector.
Statics§
- PSX_
CD_ STATS - The counters, as one block for tools that read guest memory:
StreamStatswith its field order. Updated after every interrupt and every foreground call. - PSX_
CD_ TRACE - The last 128 controller events (feature
trace):kind << 24 | phase << 16 | (flag or command) << 8 | received & 0xff. - PSX_
CD_ TRACE_ N - Events recorded so far (feature
trace); the next slot isn & 127.
Traits§
- CdHw
- Controller steps and system services the state machine needs.
Functions§
- cancel
- Cancel a read. See
Engine::cancel. - cancel_
all - Cancel everything. See
Engine::cancel_all. - configure
- Change the configuration; it applies from the next event. See
Engine::configure. (Config::time_handleris only read byinstall.) - handler_
stack_ unused_ bytes - Bytes of the handler’s private stack never touched since
installpainted it. The handler’s own frames plus the saved-context area must fit in the rest; a value near 0 means it overflowed. - install
- Take over the CD-ROM controller: put the exception wrapper in front of psx-rt’s handler and start serving requests.
- is_idle
- Whether the drive is stopped and no read holds it.
- lease_
state - Where the lease stands.
- owner
- Who may program the controller.
- phase
- The controller phase.
- queued_
count - Reads waiting behind the active one.
- release_
audio_ lease - End the lease: hand the controller back after the audio code has ended playback with Pause (not Stop). Gives the token back if there was no lease to end.
- request_
audio_ lease - Ask for the drive on behalf of CD-DA or XA playback. See
Engine::request_audio_lease; once this readsGranted, collect the controller token withtake_audio_lease. - reset_
max_ irq_ ticks - Start the longest-handler measurement over. See
Engine::reset_max_irq_ticks. - service
- Check for a stalled transfer. See
Engine::service. - state
- Where a read is. See
Engine::state. - stats
- A copy of the counters.
- submit
- Queue a read. See
Engine::submit. - take_
audio_ lease - The controller token, once the lease is granted (and not yet taken). The
audio code programs the controller through it until it hands it back with
release_audio_lease; the CD interrupt source stays closed meanwhile. - try_
take_ audio_ lease - Take the drive for audio only if it is free right now, in one step: the
controller token if the lease was granted,
Noneif a read is in flight. Nothing is left pending and the read is not aborted, so a read submitted afterwards is never parked behind a lease granted later. SeeEngine::try_audio_lease; end the lease withrelease_audio_lease. - uninstall
- Give the controller back to polled code. Succeeds only when the transport
is idle with nothing queued; call
cancel_alland pollis_idlefirst. The wrapper stays in the exception vector (a few instructions per exception) but the CD source is closed. - withdraw_
audio_ lease - Withdraw a lease that was asked for and has not been granted. Returns
whether one was pending. See
Engine::withdraw_audio_lease.