pub struct Engine<H: CdHw> { /* private fields */ }Expand description
The CD transport.
One engine owns the drive. Foreground code submits Requests; the CD
interrupt handler calls Engine::on_interrupt once per interrupt, and
each call advances the machine by one controller event. Nothing in the
machine waits for the drive: a command is written and the machine returns,
and the response arrives as the next interrupt.
§A transfer
Setloc ──INT3──▶ SeekL ──INT3──▶ (seek) ──INT2──▶ Setmode(double speed)
──INT3──▶ ReadN ──INT3──▶ INT1 × n (one sector each) ──▶ Pause
──INT3──▶ (stop) ──INT2──▶ stoppedThe read is seek-first (Setloc, SeekL, then Setmode and ReadN), the form the BIOS uses and the only one proven to stream byte-perfect on a console: a bare Setloc plus ReadN starts delivering while the mechanism is still settling. Each INT1 pops one sector into the request’s destination. After a failure the drive’s state is unknown, so the next transfer opens with a recovery Pause.
§Chaining
When the last sector of a request lands and the best queued request starts
at the next sector, the machine swaps the destination inside that same
interrupt and keeps reading: no Pause, no seek. A request is Done the
moment its last sector lands, even though the drive is still running on
to the Pause; Engine::is_idle says when it has stopped.
§Abort
Engine::cancel only sets a flag. The handler drops the next sector it
pops and issues Pause itself. Pausing from the foreground while sectors
keep arriving lost the Pause acknowledge on silicon (no interrupt for 600
VBlanks), so the foreground never does it.
§Generic over the drive
On the console the one instance lives in the crate’s global and is reached through the free functions at the crate root; on the host a scripted drive stands in.
Implementations§
Source§impl<H: CdHw> Engine<H>
impl<H: CdHw> Engine<H>
Sourcepub const fn new(hw: H, config: Config) -> Self
pub const fn new(hw: H, config: Config) -> Self
An engine in the Owner::Boot state: nothing runs until
attach.
Sourcepub fn configure(&mut self, config: Config)
pub fn configure(&mut self, config: Config)
Replace the configuration. It applies from the next event.
Sourcepub fn stats(&self) -> StreamStats
pub fn stats(&self) -> StreamStats
A copy of the counters, with the current phase and error filled in.
Sourcepub fn reset_max_irq_ticks(&mut self)
pub fn reset_max_irq_ticks(&mut self)
Start the longest-handler measurement over
(StreamStats::max_irq_ticks to 0), to attribute the peak to the
next stretch of work.
Sourcepub fn queued_count(&self) -> usize
pub fn queued_count(&self) -> usize
Requests waiting behind the active one.
Sourcepub fn is_idle(&self) -> bool
pub fn is_idle(&self) -> bool
Whether the drive is stopped and no request holds it. Queued requests
may still be waiting for their turn (while the owner is not Data).
Sourcepub fn attach(&mut self)
pub fn attach(&mut self)
Take the drive: Owner::Boot becomes Owner::Data and queued
requests start.
The controller must be in a clean state: the polled reader released, no command in flight. A no-op in any other state.
Sourcepub fn detach(&mut self) -> bool
pub fn detach(&mut self) -> bool
Hand the drive back to polled code. Succeeds only when it is idle and
nothing is queued or wanted; cancel_all and wait
for is_idle first.
Sourcepub fn lease_state(&self) -> LeaseState
pub fn lease_state(&self) -> LeaseState
Where an audio lease stands.
Sourcepub fn request_audio_lease(&mut self) -> LeaseState
pub fn request_audio_lease(&mut self) -> LeaseState
Ask for the drive on behalf of CD-DA or XA playback.
New requests are still accepted but queue. A request in flight is
aborted at its next sector (it ends Outcome::Cancelled with what
landed; resume it with Request::remaining_after after the lease).
When the drive has stopped the lease is granted: the CD interrupt
source is masked, and the audio code may program the controller
directly. Poll lease_state until it reads
LeaseState::Granted if this returns Pending.
Before attach the program already holds the drive
and this returns Granted without changing anything.
Sourcepub fn try_audio_lease(&mut self) -> bool
pub fn try_audio_lease(&mut self) -> bool
Take the drive for audio only if it can be had right now, without waiting and without leaving anything behind.
Returns true when the lease is granted (the drive was idle, or the
program still holds it before attach). Returns
false when a request is in flight: nothing changes, no lease is left
pending and the request is not aborted, so a read submitted afterwards
cannot end up queued behind a lease that is granted later. Ask again
when the drive is idle.
request_audio_lease is for audio that
must displace a read; this is for audio that only wants the drive
while nobody else is using it.
Sourcepub fn withdraw_audio_lease(&mut self) -> bool
pub fn withdraw_audio_lease(&mut self) -> bool
Withdraw a lease that was asked for and not yet granted. Returns whether one was pending.
Call it before submitting a read that must not wait for audio: a
pending lease is granted the moment the drive stops, which parks every
read queued behind it until the audio code gives the drive back. The
request that was aborted for the lease still ends
Outcome::Cancelled (resume it with Request::remaining_after).
A lease that is already granted is ended with
release_audio_lease.
Sourcepub fn release_audio_lease(&mut self) -> bool
pub fn release_audio_lease(&mut self) -> bool
Give the drive back after audio playback, or withdraw a lease that is still pending. Returns whether there was a lease to end.
The audio code must have ended playback with Pause (never Stop: the
motor then spins down, reports status 0 for a second or two, and reads
started in that window failed on a console). The next transfer starts
with a recovery Pause if Config::pause_after_audio is set.
Sourcepub fn submit(&mut self, request: Request) -> Result<Ticket, SubmitError>
pub fn submit(&mut self, request: Request) -> Result<Ticket, SubmitError>
Queue a request. It starts at once when the drive is free.
Higher priority (lower number) goes first; equal priorities go in submission order, except that one starting at the sector after the active request’s last is picked ahead of its peers so it chains without a seek.
Sourcepub fn cancel(&mut self, ticket: Ticket) -> bool
pub fn cancel(&mut self, ticket: Ticket) -> bool
Stop a request. Returns whether ticket was queued or active.
A queued request is dropped at once (Outcome::Cancelled, 0
sectors). The active one is flagged: the handler drops the next
sector, pauses the drive, and the request ends Cancelled with the
sectors that had landed.
Sourcepub fn cancel_all(&mut self)
pub fn cancel_all(&mut self)
cancel everything: the queue and the active request.
Sourcepub fn state(&mut self, ticket: Ticket) -> RequestState
pub fn state(&mut self, ticket: Ticket) -> RequestState
Where ticket is. Also runs the no-progress check (service).
Sourcepub fn service(&mut self)
pub fn service(&mut self)
Foreground upkeep: fail a transfer that has made no progress for
Config::timeout_vblanks. state calls this; a main
loop that never asks about a ticket should call it once per frame.
Sourcepub fn on_interrupt(&mut self)
pub fn on_interrupt(&mut self)
Handle one CD interrupt. Called by the interrupt handler (the crate’s exception wrapper on the console, the test harness on the host).
Sourcepub fn source_wanted(&self) -> bool
pub fn source_wanted(&self) -> bool
Whether the CD interrupt source should reach the CPU right now.
Sourcepub fn close_source(&mut self)
pub fn close_source(&mut self)
Close the CPU-side CD source. The console wrapper calls this before it
touches the engine from the foreground, so the handler cannot run
meanwhile; every foreground method ends by opening it again if
source_wanted.
Sourcepub fn note_source_closed(&mut self)
pub fn note_source_closed(&mut self)
Record that the source was closed behind the engine’s back (by the
console wrapper, through I_MASK directly), so the next
sync_source reopens it.
Sourcepub fn sync_source(&mut self)
pub fn sync_source(&mut self)
Open or close the source to match source_wanted.