Skip to main content

rustyn64_audio/
lib.rs

1//! `rustyn64-audio` — the Audio Interface (AI) DAC + sample-DMA path.
2//!
3//! The RSP's audio microcode mixes samples into an RDRAM buffer; the AI then
4//! DMAs that buffer out to the DAC at a programmable sample rate (set via the
5//! `AI_DACRATE` divider off the video clock) and raises an AI interrupt when a
6//! buffer **starts** playing (not when it drains — see [`Audio::write_reg`]).
7//! This crate models the AI side; the actual mixing is RSP microcode (in
8//! `rustyn64-rsp`), so under LLE the audio "falls out free" (ADR 0002).
9//!
10//! The model follows the hardware description in
11//! `n64brew_wiki/markdown/Audio Interface.md` and the reference behavior in
12//! ares (ISC) `ref-proj/ares/ares/n64/ai/`: a two-deep DMA FIFO, the
13//! delayed-carry address bug reproduced as a 13-bit/11-bit split with a
14//! one-sample-deferred carry, the `AI_LENGTH` mirror on every write-only
15//! register, and a DAC that decays toward silence on underrun.
16//!
17//! Part of the one-directional chip-crate graph (see `docs/architecture.md`):
18//! this crate does NOT depend on any other chip crate; it reaches RDRAM and the
19//! interrupt line through the [`AudioBus`] trait. `#![no_std]` + `alloc`.
20
21#![no_std]
22#![forbid(unsafe_code)]
23#![warn(missing_docs)]
24// The DAC deliberately reinterprets the 32-bit RDRAM word's halves as signed
25// 16-bit samples, so a wrapping u->i cast is the intended operation.
26#![allow(
27    clippy::cast_possible_truncation,
28    clippy::cast_lossless,
29    clippy::cast_possible_wrap
30)]
31
32extern crate alloc;
33
34use alloc::vec::Vec;
35use serde::{Deserialize, Serialize};
36
37/// The canonical master clock (`MASTER_HZ`, ADR 0006), duplicated here because
38/// the chip-crate graph forbids `rustyn64-audio` depending on `rustyn64-core`.
39///
40/// The DAC period (master ticks per output sample) is `MASTER_HZ / sample_rate`.
41/// A cross-crate test in `rustyn64-core` asserts this equals the scheduler's
42/// `MASTER_HZ`, so the two cannot drift.
43pub const MASTER_HZ: u64 = 187_500_000;
44
45/// Video clock feeding the AI DAC divider on **NTSC** consoles (Hz).
46///
47/// The sample rate is this divided by `AI_DACRATE + 1`, so `AI_DACRATE = 1103`
48/// yields ~44.1 kHz. Provenance: project64/N64-Tests `DoubleShot` (which
49/// computes `(VI_NTSC_CLOCK / FREQ) - 1`) and the N64brew wiki. Documented, not
50/// tuned — accuracy-ledger entry for the region video clock.
51pub const VIDEO_CLOCK_NTSC: u32 = 48_681_812;
52
53/// Video clock feeding the AI DAC divider on **PAL** consoles (Hz).
54///
55/// Same derivation as [`VIDEO_CLOCK_NTSC`]; the differing clock is why the same
56/// `AI_DACRATE` detunes between regions. PAL VI *cadence* is still residual R-6;
57/// this constant is only the AI divisor.
58pub const VIDEO_CLOCK_PAL: u32 = 49_656_530;
59
60/// The emulated console region, selecting the AI video clock.
61#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
62pub enum Region {
63    /// NTSC (~60 Hz), the common region.
64    #[default]
65    Ntsc,
66    /// PAL (~50 Hz).
67    Pal,
68}
69
70impl Region {
71    /// The video clock (Hz) this region feeds into the AI DAC divider.
72    #[must_use]
73    pub const fn video_clock(self) -> u32 {
74        match self {
75            Self::Ntsc => VIDEO_CLOCK_NTSC,
76            Self::Pal => VIDEO_CLOCK_PAL,
77        }
78    }
79
80    /// The region a cartridge's **destination code** (ROM header byte `0x3E`, the
81    /// last character of the 4-byte game code) implies.
82    ///
83    /// The destination characters are the N64brew Wiki *ROM Header* §Standard
84    /// header table (read from `n64brew_wiki/html/ROM Header.xhtml` — the markdown
85    /// mirror renders that table as a bare `[TABLE]` placeholder):
86    ///
87    /// `A` All · `B` Brazil · `C` China · `D` Germany · `E` North America ·
88    /// `F` France · `G` Gateway 64 (NTSC) · `H` Netherlands · `I` Italy ·
89    /// `J` Japan · `K` Korea · `L` Gateway 64 (PAL) · `N` Canada · `P` Europe ·
90    /// `S` Spain · `U` Australia · `W` Scandinavia · `X`/`Y`/`Z` Europe
91    ///
92    /// **Provenance, and its limit.** That table gives *destinations*, **not TV
93    /// standards** — only `G`/`L` are labeled NTSC/PAL by the wiki itself. The
94    /// 50/60 Hz classification of the remaining countries is the **broadcast
95    /// standard for each territory**, not a hardware-documented N64 fact, and
96    /// there is **no oracle** for it (no test ROM checks region detection). It is
97    /// therefore modeled and ledgered as documented-but-ungated rather than
98    /// presented as measured — see `docs/accuracy-ledger.md` (T-71-005).
99    ///
100    /// Three cases are decided explicitly rather than guessed:
101    /// - **`B` Brazil → NTSC.** Brazil broadcast PAL-**M**, which is a *60 Hz*
102    ///   standard; for the AI divisor it behaves as NTSC, not as 50 Hz PAL.
103    /// - **`C` China → NTSC.** A PAL territory, but with no known retail N64; the
104    ///   default is kept rather than inventing a 50 Hz cartridge that never shipped.
105    /// - **`A` "All" → NTSC.** Names no single region, so it takes the default.
106    ///
107    /// Any unrecognized byte also returns [`Region::Ntsc`], so an unknown or
108    /// homebrew code is behavior-preserving rather than silently retuning audio.
109    #[must_use]
110    pub const fn from_destination_code(code: u8) -> Self {
111        match code {
112            // Europe and the 50 Hz territories.
113            b'D' | b'F' | b'H' | b'I' | b'L' | b'P' | b'S' | b'U' | b'W' | b'X' | b'Y' | b'Z' => {
114                Self::Pal
115            }
116            // North America, Japan, Korea, Canada, Gateway-NTSC, Brazil (PAL-M is
117            // 60 Hz), China (no retail N64), and "All" — plus every unknown byte.
118            _ => Self::Ntsc,
119        }
120    }
121}
122
123/// The narrow bus the AI sees (`RustyNES`'s `ApuBus` analog): fetch a DMA sample
124/// word from RDRAM and raise the AI interrupt when a buffer starts.
125pub trait AudioBus {
126    /// Fetch a big-endian 32-bit sample word (two 16-bit L/R samples) from the
127    /// AI DMA buffer in RDRAM at `addr`.
128    fn ai_dma_read_u32(&self, addr: u32) -> u32;
129    /// Raise the AI interrupt on the MI (a queued buffer became active). Default
130    /// no-op so a non-interrupt bus can still drive the DAC.
131    fn raise_ai_interrupt(&mut self) {}
132}
133
134/// One stereo output frame (interleaved signed 16-bit L, R).
135#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
136pub struct StereoSample {
137    /// Left channel.
138    pub left: i16,
139    /// Right channel.
140    pub right: i16,
141}
142
143/// The result an AI register write hands back to the Bus, which owns the MI
144/// interrupt lines the AI itself cannot name.
145#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
146pub enum AiIrq {
147    /// The write had no interrupt effect.
148    #[default]
149    None,
150    /// Raise `MI_INTR.ai` — the first buffer of an idle queue was enqueued and
151    /// starts immediately.
152    Raise,
153    /// Lower `MI_INTR.ai` — a write to `AI_STATUS` acknowledges the interrupt.
154    Lower,
155}
156
157/// Proof that a step needs the bus, produced only by [`Audio::tick_without_bus`]
158/// and consumed only by [`Audio::tick_with_bus`].
159///
160/// The two halves exist for a caller that cannot lend this struct out without
161/// first moving it, and so needs to know whether the step will touch RDRAM
162/// *before* paying for the move. At a typical ~32 kHz that is about one step in
163/// 1,950. (In this workspace that caller is the Bus, which owns every chip.)
164///
165/// The fields are private and the type has no constructor, so it cannot be forged.
166/// It carries the DAC period as well as the proof, which keeps the 64-bit divide
167/// that produced it from being repeated in the second half.
168///
169/// **`Copy` and `Clone` are deliberately not derived**, and it is taken by value:
170/// either would let a caller keep a token past the step it authorized and present
171/// it again once `next_sample_tick` had moved on. `Debug` is derived because it
172/// cannot duplicate the value.
173///
174/// **Dropping a token loses the samples that were due**, unlike the RDP's
175/// equivalent: `tick_without_bus` has already stamped `last_tick`, and the `while`
176/// in the second half is what advances `next_sample_tick`, so a dropped token
177/// leaves those samples unemitted and the schedule behind `now` — the next call
178/// then emits them late in a burst.
179///
180/// What makes ignoring one loud is the `#[must_use]` on
181/// [`Audio::tick_without_bus`] **itself**, not the one on this type: an attribute
182/// on `T` does not propagate through `Option<T>`, and `Option` — unlike `Result`
183/// — is not `#[must_use]` either.
184///
185/// The attribute here is **dormant, and kept only for the day the return type is
186/// not wrapped** — it does not catch a caller who binds the token and drops it.
187/// That was proposed in review and probed rather than assumed: with
188/// `if let Some(_proof) = ai.tick_without_bus(1) {}`, `cargo clippy --all-targets`
189/// reports **zero** warnings both with and without it. A type-level `#[must_use]`
190/// fires on an unused *expression*, not on a binding; a bound-then-dropped value
191/// is `unused_variables`, which the leading underscore silences either way.
192#[derive(Debug)]
193#[must_use]
194pub struct NeedsBus {
195    /// Master ticks between output samples, already divided.
196    period: u64,
197    /// The tick the step is advancing to, as passed to `tick_without_bus`.
198    now: u64,
199}
200
201/// Audio Interface state: the two-deep DMA FIFO, the DAC rate divider, and the
202/// derived-timing sample emission.
203#[derive(Debug, Clone, Serialize, Deserialize)]
204pub struct Audio {
205    // --- The two-deep DMA FIFO (front = index 0). ---
206    /// RDRAM base of each queued transfer (24-bit, 8-byte aligned). Slot 0 is
207    /// the transfer currently draining.
208    dma_addr: [u32; 2],
209    /// Remaining bytes of each queued transfer (18-bit, 8-byte aligned).
210    dma_len: [u32; 2],
211    /// Number of queued transfers, 0..=2. `BUSY` = `> 0`, `FULL` = `> 1`.
212    dma_count: u8,
213    /// Deferred carry into `dma_addr[0]` bits 13..=23 — the delayed-carry bug.
214    /// Set when the low 13 bits wrap past an `0x2000` page, applied one sample
215    /// later (which is the *next transfer* when it wraps on the final sample).
216    addr_carry: u32,
217    /// `AI_CONTROL` bit 0.
218    dma_enable: bool,
219
220    // --- The DAC rate. ---
221    /// `AI_DACRATE` (14-bit sample-period divider).
222    dac_rate: u16,
223    /// `AI_BITRATE` (4-bit half-bit-clock divider).
224    bit_rate: u8,
225    /// The region video clock (Hz) the DAC rate divides.
226    video_clock: u32,
227    /// Derived output sample rate in Hz, `video_clock / (dac_rate + 1)`, or
228    /// [`Audio::DEFAULT_DAC_HZ`] before `AI_DACRATE` is programmed.
229    ///
230    /// Never 0 on a constructed machine: a zero rate stops the DAC, and a
231    /// stopped DAC cannot retire a queued transfer (ledger R-16).
232    sample_rate: u32,
233
234    // --- Derived-timing emission (ADR 0006: everything off `master_ticks`). ---
235    /// Master tick at which the next output sample is due; 0 = unanchored.
236    next_sample_tick: u64,
237    /// The most recent `master_ticks` the DAC was advanced to (drives the
238    /// `AI_STATUS` `COUNT`/`WC` readback without threading the clock into reads).
239    last_tick: u64,
240    /// Last emitted sample, held and decayed on underrun so the DAC does not
241    /// hard-stop (matches ares' decay-to-silence behavior).
242    dac_hold: StereoSample,
243    /// Count of buffer starvations (a transfer drained with none queued behind
244    /// it) — observable so a resampler cannot silently paper over underrun.
245    underruns: u64,
246
247    /// The emitted stereo stream on the emulated timeline, drained per frame by
248    /// the frontend (which resamples to the host rate — ADR 0004).
249    sink: Vec<StereoSample>,
250}
251
252impl Default for Audio {
253    fn default() -> Self {
254        Self::new()
255    }
256}
257
258impl Audio {
259    /// The 8 KiB (`0x2000`) page whose crossing arms the delayed-carry bug.
260    const PAGE: u32 = 0x2000;
261
262    /// DAC rate used while `AI_DACRATE` is unprogrammed.
263    ///
264    /// **A modeling default, not a measured hardware value — see ledger R-16.**
265    /// `AI_DACRATE`'s reset value is not documented anywhere this project
266    /// mirrors, so no rate can be *derived* for the pre-programmed window. What
267    /// matters, and what IS established, is structural: the DAC has no stopped
268    /// state, so the transfer queue must be able to retire before software
269    /// programs the rate. This value is taken from ares (ISC, vendorable), whose
270    /// `AI::power()` sets `dac.frequency = 44100`.
271    ///
272    /// Nothing observable should depend on the exact number: every title
273    /// programs `AI_DACRATE` before it plays anything, so this rate only governs
274    /// how fast an unprogrammed DAC drains silence. If a future change makes an
275    /// output depend on it, that dependency is the bug, not this constant.
276    const DEFAULT_DAC_HZ: u32 = 44_100;
277
278    /// Construct at power-on (NTSC, idle).
279    #[must_use]
280    pub const fn new() -> Self {
281        let mut ai = Self {
282            dma_addr: [0; 2],
283            dma_len: [0; 2],
284            dma_count: 0,
285            addr_carry: 0,
286            dma_enable: false,
287            dac_rate: 0,
288            bit_rate: 0,
289            video_clock: VIDEO_CLOCK_NTSC,
290            // Placeholder only — `recompute_rate` below derives the real value.
291            sample_rate: 0,
292            next_sample_tick: 0,
293            last_tick: 0,
294            dac_hold: StereoSample { left: 0, right: 0 },
295            underruns: 0,
296            sink: Vec::new(),
297        };
298        // Derive the power-on rate through the SAME function every later rate
299        // change uses, rather than repeating its `dac_rate == 0` decision here.
300        // The duplicate literal this replaces was only kept correct by a comment
301        // saying "must match `recompute_rate`" — precisely the comment-enforced
302        // invariant this project distrusts, and the drift would be silent: a
303        // constructor left at 0 puts the machine back in the stopped-DAC state
304        // ledger R-16's livelock needs, and no test of a *programmed* DAC would
305        // notice. Raised in review on #205.
306        ai.recompute_rate();
307        ai
308    }
309
310    /// Select the console region (sets the video clock and re-derives the rate).
311    /// Wired from the cart header at ROM load; defaults to NTSC.
312    pub const fn set_region(&mut self, region: Region) {
313        self.video_clock = region.video_clock();
314        self.recompute_rate();
315    }
316
317    /// Observed underrun count (buffer starvations) — for the harness.
318    #[must_use]
319    pub const fn underruns(&self) -> u64 {
320        self.underruns
321    }
322
323    /// The derived output sample rate in Hz (0 until `AI_DACRATE` is set).
324    #[must_use]
325    pub const fn sample_rate(&self) -> u32 {
326        self.sample_rate
327    }
328
329    /// Drain the emitted stereo stream produced since the last drain.
330    pub fn drain(&mut self) -> Vec<StereoSample> {
331        core::mem::take(&mut self.sink)
332    }
333
334    /// Read an AI register (`index` = `(addr >> 2) & 7`).
335    ///
336    /// Every register except `AI_STATUS` (index 3) is write-only and reads back
337    /// a mirror of `AI_LENGTH` (the front transfer's remaining bytes), per the
338    /// wiki and ares. `AI_STATUS` reports the FULL/BUSY/ENABLED flags plus the
339    /// best-effort `COUNT`/`WC` readback (ledgered — no oracle pins its phase).
340    #[must_use]
341    pub fn read_reg(&self, index: u32) -> u32 {
342        if index == 3 {
343            self.status()
344        } else {
345            // AI_LENGTH mirror: remaining bytes of the active transfer.
346            self.dma_len[0] & 0x0003_FFFF
347        }
348    }
349
350    /// Assemble `AI_STATUS`.
351    ///
352    /// FULL (bit 31 and bit 0), BUSY (bit 30), and ENABLED (bit 25) are the
353    /// flags software polls and are exact. Bits 20 and 24 read as 1 on hardware
354    /// (ares). `COUNT` (bits 14..=1) and `WC` (bit 19) are a best-effort model
355    /// of the DAC's internal down-counter — striven, not gated (ledgered),
356    /// because no public capture pins their exact phase.
357    fn status(&self) -> u32 {
358        let mut s = 0u32;
359        if self.dma_count > 1 {
360            s |= 1 << 31; // FULL
361            s |= 1 << 0; // FULL (mirror copy)
362        }
363        if self.dma_count > 0 {
364            s |= 1 << 30; // BUSY
365        }
366        s |= 1 << 24; // always 1 (ares)
367        if self.dma_enable {
368            s |= 1 << 25; // ENABLED
369        }
370        s |= 1 << 20; // always 1 (ares)
371        // COUNT ticks down at the VI clock, from DACRATE/2 to 0, reloading; WC
372        // (LRCK) toggles at DACRATE/2, out of phase by half a period. Both are
373        // gated by BITRATE != 0. Derived from `last_tick`; see ledger.
374        // Gated on `AI_BITRATE` ALONE: the wiki says the counter "always ticks
375        // unless `AI_BITRATE` is 0" and that a `BITRATE` of 0 "stops the clock" —
376        // neither is said to depend on `AI_DACRATE`. `COUNT`/`WC` additionally need
377        // a DAC rate to have a range at all, which their own guard below supplies.
378        if self.bit_rate != 0 {
379            // All three readbacks are phases of the same video-clock tick count.
380            let vi_ticks = self.last_tick.saturating_mul(u64::from(self.video_clock)) / MASTER_HZ;
381            let half = u64::from(self.dac_rate) / 2;
382            if half != 0 {
383                let phase = vi_ticks % (half * 2);
384                let count = if phase < half {
385                    half - phase
386                } else {
387                    half * 2 - phase
388                };
389                s |= ((count as u32) & 0x3FFF) << 1;
390                if phase >= half {
391                    s |= 1 << 19; // WC high on the second half-period
392                }
393            }
394            // BC (bit 16) — the BCLK line to the BU9480 DAC. `AI_BITRATE` is
395            // "Half of bit clock period" and "the bit clock rate is the Video
396            // clock, divided by two, divided by one more than this number"
397            // (wiki §AI_BITRATE), so one half-period is `BITRATE + 1` video
398            // clocks and the line toggles once per half-period. A `BITRATE` of 0
399            // stops the clock, which the enclosing gate excludes — and note that
400            // gate is BITRATE-only, so BCLK keeps running with no DAC rate set.
401            //
402            // The wiki itself hedges this ("believed", "probably") because the
403            // CPU "cannot reliably sample it rapidly enough even when BITRATE is
404            // set to 15" — so it is un-observable by software in practice and
405            // stays ungated (ledger R-16), like `COUNT`/`WC`.
406            let half_periods = vi_ticks / (u64::from(self.bit_rate) + 1);
407            if half_periods & 1 != 0 {
408                s |= 1 << 16; // BC
409            }
410        }
411        s
412    }
413
414    /// Write an AI register (`index` = `(addr >> 2) & 7`), returning the MI
415    /// interrupt effect for the Bus to apply.
416    ///
417    /// The **interrupt fires when a transfer starts, not when it ends**: writing
418    /// `AI_LENGTH` into an idle queue (`dma_count == 0`) starts that buffer
419    /// immediately and raises the interrupt now; a second buffer queued behind a
420    /// playing one raises nothing until it is promoted in [`Audio::tick`]. This
421    /// is what lets software refill during playback (wiki §DMA).
422    pub fn write_reg(&mut self, index: u32, val: u32) -> AiIrq {
423        match index {
424            0 => {
425                // AI_DRAM_ADDR: stage the next free slot's base (24-bit, & ~7).
426                if self.dma_count < 2 {
427                    self.dma_addr[self.dma_count as usize] = val & 0x00FF_FFF8;
428                }
429                AiIrq::None
430            }
431            1 => {
432                // AI_LENGTH: stage the next free slot's length (18-bit, & ~7)
433                // and enqueue it. Enqueueing into an idle queue starts playback.
434                let length = val & 0x0003_FFF8;
435                if self.dma_count < 2 {
436                    let starting = self.dma_count == 0;
437                    self.dma_len[self.dma_count as usize] = length;
438                    self.dma_count += 1;
439                    if starting {
440                        // The buffer starts now; anchor the first sample one
441                        // period out from the DAC's current position and fire
442                        // the IRQ (it fires on *start*, not drain).
443                        self.addr_carry = 0;
444                        self.next_sample_tick = self.last_tick.saturating_add(self.period_ticks());
445                        return AiIrq::Raise;
446                    }
447                }
448                AiIrq::None
449            }
450            2 => {
451                // AI_CONTROL: DMA enable (bit 0).
452                self.dma_enable = val & 1 != 0;
453                AiIrq::None
454            }
455            3 => AiIrq::Lower, // AI_STATUS write acknowledges the interrupt.
456            4 => {
457                // AI_DACRATE (14-bit): re-derive the sample rate.
458                self.dac_rate = (val & 0x3FFF) as u16;
459                self.recompute_rate();
460                AiIrq::None
461            }
462            5 => {
463                // AI_BITRATE (4-bit).
464                self.bit_rate = (val & 0xF) as u8;
465                AiIrq::None
466            }
467            _ => AiIrq::None, // indices 6/7 are unmapped.
468        }
469    }
470
471    /// Re-derive [`Audio::sample_rate`] from the current video clock and
472    /// `AI_DACRATE`.
473    const fn recompute_rate(&mut self) {
474        // An unprogrammed `AI_DACRATE` falls back to [`Self::DEFAULT_DAC_HZ`]
475        // rather than to zero. Zero used to mean "emit nothing", which is wrong
476        // in a way that reaches far past audio: with the DAC stopped, `tick()`
477        // returns before `emit_sample`, and `emit_sample` is the ONLY place a
478        // drained transfer is retired — so `AI_STATUS.FULL` could latch and never
479        // clear, and a game polling it for a free DMA slot spun forever. World
480        // Driver Championship did exactly that (ledger R-16).
481        //
482        // What is ESTABLISHED and what is INFERRED, kept apart deliberately —
483        // the reset semantics are admitted undocumented below, and an inference
484        // dressed as a hardware fact is what this ledger exists to prevent.
485        //
486        // ESTABLISHED (readable reference, ISC): ares runs its DAC from power-on.
487        // `AI::power()` sets `dac.frequency = 44100` and `AI::main()` calls
488        // `sample()` unconditionally, so its equivalent retirement block runs
489        // before any `AI_DACRATE` write.
490        // INFERRED (from that, plus a divider having no "off" encoding): the
491        // hardware DAC counter likewise has no stopped state.
492        // NOT ESTABLISHED: what `AI_DACRATE` holds at reset. No source this
493        // project mirrors says, which is exactly why the rate is labeled a
494        // modeling default and not a measured value.
495        //
496        // The naive alternative — letting `dac_rate == 0` compute
497        // `video_clock / 1` ≈ 48 MHz — is what the old zero-gate was avoiding,
498        // and it would flood the sink. A default rate avoids both failures.
499        //
500        // TODO(T-AUDIO-01): separate an explicit `AI_DACRATE = 0` write from the
501        // unprogrammed reset state. Needs a `dac_rate_programmed` field, hence a
502        // save-state layout bump (ADR 0005) — deferred, see below and ledger R-16.
503        //
504        // KNOWN SIMPLIFICATION, ledgered (R-16): this conflates "never
505        // programmed" with "software explicitly wrote 0". ares keeps them apart —
506        // `power()` sets 44100, while its `AI_DACRATE` write honors a literal
507        // zero (`dac.frequency = max(1, videoFrequency / (dacRate + 1))`,
508        // `ai/io.cpp`) — so full fidelity needs a `dac_rate_programmed` flag to
509        // tell the two apart. That adds a field to a serialized struct and so
510        // changes the save-state layout (ADR 0005), which is not a change to make
511        // in passing. Unobservable in practice: a DACRATE of 0 asks for a ~48 MHz
512        // DAC and no title does it. Recorded rather than silently accepted.
513        self.sample_rate = if self.video_clock == 0 {
514            0
515        } else if self.dac_rate == 0 {
516            Self::DEFAULT_DAC_HZ
517        } else {
518            self.video_clock / (self.dac_rate as u32 + 1)
519        };
520    }
521
522    /// Master ticks between output samples, `MASTER_HZ / sample_rate`.
523    ///
524    /// Zero only when the video clock is unset, which cannot happen on a
525    /// constructed machine. It is **no longer** zero for an unprogrammed
526    /// `AI_DACRATE` — that falls back to [`Self::DEFAULT_DAC_HZ`], because a
527    /// stopped DAC cannot retire a transfer and latched `AI_STATUS.FULL`
528    /// (ledger R-16).
529    fn period_ticks(&self) -> u64 {
530        if self.sample_rate == 0 {
531            0
532        } else {
533            (MASTER_HZ / u64::from(self.sample_rate)).max(1)
534        }
535    }
536
537    /// Advance the AI to `now` master ticks, emitting every output sample whose
538    /// scheduled tick has arrived.
539    ///
540    /// Derived timing (ADR 0006): the number of samples emitted is a function of
541    /// `now` and the DAC period, never of an independently incremented counter.
542    /// Hot path — allocation into the sink is the only cost while playing.
543    pub fn tick<B: AudioBus>(&mut self, now: u64, bus: &mut B) {
544        if let Some(proof) = self.tick_without_bus(now) {
545            self.tick_with_bus(proof, bus);
546        }
547    }
548
549    /// The part of a step that needs **no bus access**, returning `None` when it
550    /// finished the step on its own and `Some(NeedsBus)` when samples must be read
551    /// out of RDRAM.
552    ///
553    /// **This advances state**, despite the `Option` return: it stamps `last_tick`
554    /// on every call and anchors `next_sample_tick` on the first one. It is named
555    /// `tick_*` rather than `is_*` for that reason.
556    ///
557    /// Split out so a caller can decide whether to pay for bus access *before*
558    /// arranging it. A caller that owns this struct cannot lend it out without
559    /// first moving it, and that move is pure overhead on the ~99.95% of steps
560    /// that emit nothing (`docs/audio.md` §Derived timing). [`Audio::tick`] calls
561    /// this too, so there is one implementation of the early-outs and no way for
562    /// the two to disagree.
563    #[must_use = "a `Some` means samples are due and the step needs `tick_with_bus`"]
564    pub fn tick_without_bus(&mut self, now: u64) -> Option<NeedsBus> {
565        self.last_tick = now;
566        // A schedule exists and its next sample is still ahead: nothing to do, and
567        // in particular no need for `period_ticks`, whose 64-bit divide dominated
568        // this function (`docs/audio.md` §Derived timing for the cost and the
569        // reason it is an ordering change rather than a cached field).
570        //
571        // The old code returned from the same states without touching a field, so
572        // this only moves the decision earlier.
573        if self.next_sample_tick != 0 && now < self.next_sample_tick {
574            // The R-16 guard still has to be checkable on the hot path, or it
575            // would only ever be evaluated on the ~0.05% of calls that emit.
576            // `debug_assert` compiles out of release, so this costs nothing.
577            debug_assert!(
578                self.sample_rate != 0,
579                "a constructed AI must never have a stopped DAC"
580            );
581            return None;
582        }
583        let period = self.period_ticks();
584        if period == 0 {
585            // Unreachable on a constructed machine: `video_clock` is always
586            // set and an unprogrammed `AI_DACRATE` now yields
587            // `DEFAULT_DAC_HZ`, not zero (ledger R-16). Kept as a guard against
588            // a divide-by-zero rather than as a modeled DAC state.
589            //
590            // The assert makes that claim CHECKABLE instead of merely stated: a
591            // future change that reintroduces a zero rate is exactly the R-16
592            // defect, and a silent `return` is how it hid the first time.
593            debug_assert!(period > 0, "a constructed AI must never have a stopped DAC");
594            return None;
595        }
596        if self.next_sample_tick == 0 {
597            // Anchor the first sample one period out so a large `now` does not
598            // dump a backlog of silence at power-on / rate change.
599            self.next_sample_tick = now.saturating_add(period);
600            return None;
601        }
602        Some(NeedsBus { period, now })
603    }
604
605    /// The rest of the step, which reads sample words out of RDRAM.
606    ///
607    /// Reachable only with a [`NeedsBus`] from [`Audio::tick_without_bus`], so the
608    /// two halves cannot run out of order: the preconditions — a non-zero period
609    /// and at least one sample already due — are carried by the type rather than by
610    /// a comment or an assertion. The token also carries the period, so the 64-bit
611    /// divide that produced it is not repeated here.
612    // `needless_pass_by_value` is correct that a `&NeedsBus` would compile — both
613    // fields are `Copy`, so nothing here needs ownership. Taking it by value is the
614    // entire safety property: a token passed by reference stays usable, and the
615    // caller could present the same one again after `next_sample_tick` had moved
616    // past it. Ownership is what makes a token good for exactly one step, so this
617    // signature is load-bearing rather than careless.
618    #[allow(clippy::needless_pass_by_value)]
619    pub fn tick_with_bus<B: AudioBus>(&mut self, proof: NeedsBus, bus: &mut B) {
620        let NeedsBus { period, now } = proof;
621        while self.next_sample_tick <= now {
622            self.emit_sample(bus);
623            self.next_sample_tick = self.next_sample_tick.saturating_add(period);
624        }
625    }
626
627    /// Emit exactly one output sample: play from RDRAM if a transfer is active,
628    /// otherwise decay the held DAC value toward silence.
629    fn emit_sample<B: AudioBus>(&mut self, bus: &mut B) {
630        let active = self.dma_count > 0 && self.dma_len[0] > 0 && self.dma_enable;
631        if active {
632            // Apply the deferred carry into the high address bits first — this
633            // is the one-cycle-late carry that produces the +0x2000 bug when the
634            // previous sample crossed a page on the final word of a transfer.
635            let high =
636                (self.dma_addr[0] & !(Self::PAGE - 1)).wrapping_add(self.addr_carry * Self::PAGE);
637            self.dma_addr[0] = high | (self.dma_addr[0] & (Self::PAGE - 1));
638
639            let word = bus.ai_dma_read_u32(self.dma_addr[0] & 0x00FF_FFFF);
640            let sample = StereoSample {
641                left: (word >> 16) as i16,
642                right: word as i16,
643            };
644            self.dac_hold = sample;
645            self.sink.push(sample);
646
647            // Advance the low 13 bits by one word; the carry out is remembered,
648            // not applied, until the next sample.
649            let low = (self.dma_addr[0] & (Self::PAGE - 1)).wrapping_add(4);
650            self.addr_carry = u32::from(low >= Self::PAGE);
651            self.dma_addr[0] = (self.dma_addr[0] & !(Self::PAGE - 1)) | (low & (Self::PAGE - 1));
652            self.dma_len[0] -= 4;
653        } else {
654            // Underrun / idle: hold-and-decay toward zero (deterministic, no
655            // float). A DAC that keeps its rate but has nothing to play settles
656            // to silence rather than clicking off.
657            self.dac_hold.left = (i32::from(self.dac_hold.left) * 63 / 64) as i16;
658            self.dac_hold.right = (i32::from(self.dac_hold.right) * 63 / 64) as i16;
659            self.sink.push(self.dac_hold);
660        }
661
662        // A drained front transfer promotes the queued one (if any) and raises
663        // the interrupt as that buffer *starts*.
664        if self.dma_count > 0 && self.dma_len[0] == 0 {
665            self.dma_count -= 1;
666            if self.dma_count > 0 {
667                self.dma_addr[0] = self.dma_addr[1];
668                self.dma_len[0] = self.dma_len[1];
669                bus.raise_ai_interrupt();
670            } else if self.dma_enable {
671                // Ran dry with nothing queued — an observable starvation.
672                self.underruns += 1;
673            }
674        }
675    }
676}
677
678/// Returns the crate version string.
679#[must_use]
680pub const fn version() -> &'static str {
681    env!("CARGO_PKG_VERSION")
682}
683
684#[cfg(test)]
685mod tests {
686    use super::*;
687
688    /// A bus backed by a flat RDRAM image, recording interrupt raises.
689    struct TestBus {
690        ram: Vec<u8>,
691        irqs: u32,
692    }
693    impl TestBus {
694        fn new(size: usize) -> Self {
695            Self {
696                ram: alloc::vec![0u8; size],
697                irqs: 0,
698            }
699        }
700        fn write_word(&mut self, addr: u32, val: u32) {
701            let a = addr as usize;
702            self.ram[a..a + 4].copy_from_slice(&val.to_be_bytes());
703        }
704    }
705    impl AudioBus for TestBus {
706        fn ai_dma_read_u32(&self, addr: u32) -> u32 {
707            let a = addr as usize;
708            u32::from_be_bytes([
709                self.ram[a],
710                self.ram[a + 1],
711                self.ram[a + 2],
712                self.ram[a + 3],
713            ])
714        }
715        fn raise_ai_interrupt(&mut self) {
716            self.irqs += 1;
717        }
718    }
719
720    /// Program a standard NTSC ~44 kHz DAC and enable DMA.
721    fn programmed() -> Audio {
722        let mut ai = Audio::new();
723        ai.write_reg(4, 1103); // AI_DACRATE → ~44_136 Hz
724        ai.write_reg(2, 1); // AI_CONTROL: DMA enable
725        ai
726    }
727
728    /// **The FIRST `tick` only anchors the sample clock; it never emits.**
729    ///
730    /// This test used to be called `idle_tick_emits_nothing_before_dacrate` and
731    /// asserted "no rate programmed → no samples". It was **vacuous, and vacuous
732    /// before the DAC default landed**: a single `tick` returns at the
733    /// `next_sample_tick == 0` anchor branch regardless of the rate, so it passed
734    /// identically before and after a change to the very behavior it claimed to
735    /// pin — the "success and failure paths converge" trap. Renamed and re-aimed
736    /// at the rule it actually exercises, which is worth pinning on its own: the
737    /// anchor exists so a large first `now` cannot dump a backlog of silence.
738    #[test]
739    fn the_first_tick_only_anchors_the_sample_clock() {
740        let mut ai = Audio::new();
741        let mut bus = TestBus::new(0x1000);
742        ai.tick(1_000_000, &mut bus);
743        assert!(
744            ai.drain().is_empty(),
745            "the anchoring tick must not emit a backlog"
746        );
747        // And the second tick DOES emit — without this half the test would pass
748        // just as well against a DAC that never emits anything at all.
749        ai.tick(1_000_000 + MASTER_HZ / 60, &mut bus);
750        assert!(
751            !ai.drain().is_empty(),
752            "the tick after the anchor must emit at the default rate"
753        );
754    }
755
756    #[test]
757    fn dacrate_derives_rate_per_region() {
758        let mut ai = Audio::new();
759        ai.write_reg(4, 1103);
760        assert_eq!(ai.sample_rate(), VIDEO_CLOCK_NTSC / 1104);
761        ai.set_region(Region::Pal);
762        assert_eq!(ai.sample_rate(), VIDEO_CLOCK_PAL / 1104);
763    }
764
765    /// **The destination code selects the region (T-71-005).** The characters are
766    /// the N64brew ROM-header table; the three judgment calls are pinned here so
767    /// a later reader sees them as decisions rather than accidents.
768    #[test]
769    fn destination_code_selects_the_region() {
770        use Region::{Ntsc, Pal};
771        // The 50 Hz territories.
772        for c in [
773            b'D', b'F', b'H', b'I', b'L', b'P', b'S', b'U', b'W', b'X', b'Y', b'Z',
774        ] {
775            assert_eq!(
776                Region::from_destination_code(c),
777                Pal,
778                "{} should be PAL",
779                c as char
780            );
781        }
782        // The 60 Hz territories.
783        for c in [b'E', b'J', b'K', b'N', b'G'] {
784            assert_eq!(
785                Region::from_destination_code(c),
786                Ntsc,
787                "{} should be NTSC",
788                c as char
789            );
790        }
791        // The three explicit decisions (see `from_destination_code`): Brazil is
792        // PAL-M, a 60 Hz standard; China has no known retail N64; "All" names no
793        // single region.
794        assert_eq!(
795            Region::from_destination_code(b'B'),
796            Ntsc,
797            "Brazil is PAL-M/60Hz"
798        );
799        assert_eq!(
800            Region::from_destination_code(b'C'),
801            Ntsc,
802            "China: no retail N64"
803        );
804        assert_eq!(
805            Region::from_destination_code(b'A'),
806            Ntsc,
807            "\"All\" takes the default"
808        );
809        // An unknown/homebrew byte must be behavior-preserving, not retune audio.
810        assert_eq!(
811            Region::from_destination_code(0),
812            Ntsc,
813            "unknown code keeps NTSC"
814        );
815        assert_eq!(
816            Region::from_destination_code(b'?'),
817            Ntsc,
818            "unknown code keeps NTSC"
819        );
820    }
821
822    /// **The selected region actually changes the sample rate.** Guards against a
823    /// mapping that is correct but never reaches the DAC divisor.
824    #[test]
825    fn a_pal_destination_code_retunes_the_dac() {
826        let mut ai = Audio::new();
827        ai.write_reg(4, 1103); // AI_DACRATE: rate = clock / (dacrate + 1)
828        let ntsc = ai.sample_rate();
829        ai.set_region(Region::from_destination_code(b'P')); // Europe
830        assert_ne!(
831            ai.sample_rate(),
832            ntsc,
833            "a PAL cartridge must retune the DAC"
834        );
835        assert_eq!(ai.sample_rate(), VIDEO_CLOCK_PAL / 1104);
836    }
837
838    #[test]
839    fn set_region_before_dacrate_does_not_fabricate_the_video_clock_rate() {
840        // What this has always been protecting: selecting a region before
841        // AI_DACRATE is programmed must NOT compute `video_clock / 1` (~48 MHz)
842        // and flood the sink.
843        //
844        // It previously asserted the rate was exactly **0** and that the DAC
845        // "emits nothing". That over-specified the protection into a bug: a
846        // zero rate makes `tick` return before `emit_sample`, and `emit_sample`
847        // is the only place a drained transfer is retired — so `AI_STATUS.FULL`
848        // could latch forever (see
849        // `full_clears_even_when_dacrate_was_never_programmed`, and ledger R-16).
850        // The unprogrammed DAC now runs at `DEFAULT_DAC_HZ`. The assertion is
851        // therefore an ORDER-OF-MAGNITUDE bound, which is what the comment
852        // always described, rather than an exact value the hardware does not
853        // document.
854        let mut ai = Audio::new();
855        ai.set_region(Region::Pal);
856        // Two assertions, each catching something the other cannot. The equality
857        // pins the WIRING — that `recompute_rate` reads the named constant rather
858        // than an inlined literal that could drift from it. The bound pins the
859        // PROPERTY, and it is the one that catches the failure class this test was
860        // written for: any future rate derived from the video clock rather than an
861        // audio rate.
862        assert_eq!(
863            ai.sample_rate(),
864            Audio::DEFAULT_DAC_HZ,
865            "an unprogrammed DAC must run at the documented default"
866        );
867        assert!(
868            ai.sample_rate() > 0 && ai.sample_rate() < 100_000,
869            "an unprogrammed DAC runs at an audio rate, not the video clock: {}",
870            ai.sample_rate()
871        );
872        let mut bus = TestBus::new(0x1000);
873        // Prime the sample clock first. Without this the single `tick` below
874        // returns at the `next_sample_tick == 0` anchor branch, `emitted` is
875        // always 0, and the upper bound passes even with the emission path
876        // completely broken — the vacuity `the_first_tick_only_anchors_the_sample_clock`
877        // documents. Raised in review on #205.
878        ai.tick(0, &mut bus);
879        ai.tick(MASTER_HZ / 60, &mut bus); // one frame
880        let emitted = ai.drain().len();
881        // A TWO-SIDED bound, because only the pair is evidence: the upper bound
882        // rejects the ~800k samples/frame a video-clock rate would produce, and
883        // the lower bound rejects a DAC that emits nothing — which is the state
884        // that caused the R-16 livelock and which an upper bound alone accepts.
885        // 44100/60 = 735.
886        assert!(
887            (500..5_000).contains(&emitted),
888            "one frame of an unprogrammed DAC must emit ~735 samples, not a flood \
889             and not silence: {emitted}"
890        );
891    }
892
893    /// **`AI_STATUS.FULL` must be able to clear even if `AI_DACRATE` was never
894    /// programmed** — the World Driver Championship livelock (ledger R-16).
895    ///
896    /// A game may queue two buffers and poll `FULL` for a free slot before it
897    /// programs the DAC. Retirement lives in `emit_sample`, which only runs when
898    /// the DAC has a period, so a stopped DAC latched `FULL` permanently and the
899    /// poll never exited. Mutation guard: restore the `dac_rate == 0 → 0` rate
900    /// and this test hangs on `FULL` forever (it fails on the assertion below).
901    #[test]
902    fn full_clears_even_when_dacrate_was_never_programmed() {
903        let mut ai = Audio::new();
904        let mut bus = TestBus::new(0x4000);
905        // Two queued transfers, no AI_DACRATE write, DMA enabled.
906        ai.write_reg(2, 1); // AI_CONTROL: DMA enable
907        ai.write_reg(0, 0x0000_1000);
908        ai.write_reg(1, 0x40);
909        ai.write_reg(0, 0x0000_2000);
910        ai.write_reg(1, 0x40);
911        assert_ne!(ai.status() & (1 << 31), 0, "two queued → FULL");
912
913        // Advance a second of emulated time. With a running DAC the two 64-byte
914        // transfers drain almost immediately; with a stopped one, never.
915        ai.tick(MASTER_HZ, &mut bus);
916        assert_eq!(
917            ai.status() & (1 << 31),
918            0,
919            "FULL must clear once a transfer retires, even with AI_DACRATE unset"
920        );
921    }
922
923    #[test]
924    fn write_only_registers_mirror_ai_length() {
925        let mut ai = programmed();
926        ai.write_reg(0, 0x0000_1000); // AI_DRAM_ADDR
927        ai.write_reg(1, 0x40); // AI_LENGTH = 64 bytes
928        // Indices 0,1,2,4,5 all read back the AI_LENGTH mirror (remaining bytes).
929        assert_eq!(ai.read_reg(0), 0x40);
930        assert_eq!(ai.read_reg(1), 0x40);
931        assert_eq!(ai.read_reg(2), 0x40);
932        assert_eq!(ai.read_reg(4), 0x40);
933        assert_eq!(ai.read_reg(5), 0x40);
934    }
935
936    #[test]
937    fn ai_length_masks_to_eight_byte_granularity() {
938        let mut ai = programmed();
939        ai.write_reg(1, 0x47); // low 3 bits dropped → 0x40
940        assert_eq!(ai.read_reg(1), 0x40);
941    }
942
943    #[test]
944    fn first_buffer_raises_irq_on_enqueue() {
945        let mut ai = programmed();
946        assert_eq!(ai.write_reg(0, 0x100), AiIrq::None);
947        assert_eq!(
948            ai.write_reg(1, 0x40),
949            AiIrq::Raise,
950            "the first buffer starts immediately and raises the AI interrupt"
951        );
952    }
953
954    #[test]
955    fn ai_status_write_acknowledges_the_interrupt() {
956        let mut ai = programmed();
957        assert_eq!(ai.write_reg(3, 0), AiIrq::Lower);
958    }
959
960    /// **`BC` toggles at the documented bit-clock half-period (R-16).**
961    ///
962    /// The wiki gives `AI_BITRATE` as "half of bit clock period" and the bit clock
963    /// as "the Video clock, divided by two, divided by one more than this number",
964    /// so one half-period is `BITRATE + 1` video clocks and BCLK toggles once per
965    /// half-period. This counts the transitions over a fixed span and compares
966    /// against that relation derived independently from the two documented
967    /// quantities — so a wrong divisor (`BITRATE` instead of `BITRATE + 1`, or the
968    /// DAC rate) changes the count and fails.
969    #[test]
970    fn bc_toggles_at_the_documented_bit_clock_half_period() {
971        const BITRATE: u32 = 15;
972        let span_master_ticks = 20_000u64;
973
974        let mut ai = programmed();
975        ai.write_reg(5, BITRATE); // AI_BITRATE
976        let mut bus = TestBus::new(0x1000);
977
978        let mut transitions = 0usize;
979        let mut prev = ai.status() & (1 << 16) != 0;
980        for now in 1..=span_master_ticks {
981            ai.tick(now, &mut bus);
982            let bc = ai.status() & (1 << 16) != 0;
983            if bc != prev {
984                transitions += 1;
985                prev = bc;
986            }
987        }
988
989        // Expected from the documented relation alone: video clocks elapsed over
990        // the span, divided by the half-period of `BITRATE + 1` video clocks.
991        let vi_ticks = span_master_ticks * u64::from(VIDEO_CLOCK_NTSC) / MASTER_HZ;
992        let expected = vi_ticks / u64::from(BITRATE + 1);
993        assert_eq!(
994            transitions as u64,
995            expected,
996            "BC must toggle once per {} video clocks",
997            BITRATE + 1
998        );
999        assert!(
1000            transitions > 100,
1001            "the span must actually exercise the clock"
1002        );
1003    }
1004
1005    /// **The bit clock does not depend on the DAC rate.** The wiki gates the
1006    /// counter on `AI_BITRATE` alone ("It always ticks unless `AI_BITRATE` is 0")
1007    /// and never on `AI_DACRATE`, so BCLK must keep toggling with no DAC rate
1008    /// programmed — even though `COUNT`/`WC`, which need a range, report nothing.
1009    #[test]
1010    fn the_bit_clock_runs_without_a_dac_rate() {
1011        let mut ai = Audio::new();
1012        ai.write_reg(5, 15); // AI_BITRATE only — no AI_DACRATE
1013        let mut bus = TestBus::new(0x1000);
1014        let mut seen_high = false;
1015        let mut seen_low = false;
1016        for now in 1..=5_000u64 {
1017            ai.tick(now, &mut bus);
1018            let st = ai.status();
1019            if st & (1 << 16) != 0 {
1020                seen_high = true;
1021            } else {
1022                seen_low = true;
1023            }
1024            // COUNT has no range without a DAC rate, so it must stay clear.
1025            assert_eq!(st & (0x3FFF << 1), 0, "COUNT needs a DAC rate");
1026        }
1027        assert!(
1028            seen_high && seen_low,
1029            "BCLK must toggle with no DAC rate programmed"
1030        );
1031    }
1032
1033    /// **A `BITRATE` of 0 stops the bit clock.** The wiki: "A written value of 0
1034    /// instead stops the clock", so `BC` must not toggle.
1035    #[test]
1036    fn a_zero_bitrate_stops_the_bit_clock() {
1037        let mut ai = programmed();
1038        ai.write_reg(5, 0); // AI_BITRATE = 0 → clock stopped
1039        let mut bus = TestBus::new(0x1000);
1040        for now in 1..=5_000u64 {
1041            ai.tick(now, &mut bus);
1042            assert_eq!(ai.status() & (1 << 16), 0, "BC must stay low at BITRATE 0");
1043        }
1044    }
1045
1046    /// **Every bus-free early-out returns `None` on its own condition, and the one
1047    /// case that needs RDRAM returns `Some`.**
1048    ///
1049    /// A predicate that *always* skipped would pass any test that only checks the
1050    /// skip fires — and a silently-always-skipping AI is a dead DAC. So the
1051    /// positive case is asserted here beside the negatives, and the token it
1052    /// returns is consumed, which is the only way to reach the second half.
1053    ///
1054    /// `last_tick` is checked on the skip paths too: the bus-free half must still
1055    /// *advance* on the steps it declines to finish, or the DAC stops tracking the
1056    /// clock while appearing to work.
1057    #[test]
1058    fn every_bus_free_early_out_fires_on_its_own_condition() {
1059        let mut bus = TestBus::new(0x1_0000);
1060
1061        // 1. A schedule exists and its next sample is still ahead.
1062        let mut ai = programmed();
1063        ai.write_reg(0, 0x100);
1064        ai.write_reg(1, 8);
1065        let due = ai.next_sample_tick;
1066        assert!(
1067            ai.tick_without_bus(due - 1).is_none(),
1068            "no bus needed before the boundary"
1069        );
1070        assert_eq!(ai.last_tick, due - 1, "the skipped step still advances");
1071
1072        // 2. No schedule yet: the first sample is anchored without touching RDRAM.
1073        let mut fresh = programmed();
1074        assert!(
1075            fresh.tick_without_bus(7).is_none(),
1076            "anchoring needs no bus"
1077        );
1078        assert_ne!(fresh.next_sample_tick, 0, "and it did anchor");
1079
1080        // 3. A sample is due — this one does need the bus, and the token proves it.
1081        bus.write_word(0x100, 0x0001_0002);
1082        bus.write_word(0x104, 0x0003_0004);
1083        let proof = ai
1084            .tick_without_bus(due)
1085            .expect("a due sample must ask for the bus");
1086        ai.tick_with_bus(proof, &mut bus);
1087        assert_eq!(
1088            ai.drain(),
1089            [StereoSample { left: 1, right: 2 }],
1090            "the second half emits the sample the token was issued for"
1091        );
1092    }
1093
1094    #[test]
1095    fn status_reports_busy_and_full() {
1096        let mut ai = programmed();
1097        ai.write_reg(0, 0x100);
1098        ai.write_reg(1, 0x40); // one queued → BUSY
1099        assert_ne!(ai.status() & (1 << 30), 0, "BUSY");
1100        assert_eq!(ai.status() & (1 << 31), 0, "not FULL with one buffer");
1101        ai.write_reg(0, 0x200);
1102        ai.write_reg(1, 0x40); // two queued → FULL
1103        assert_ne!(ai.status() & (1 << 31), 0, "FULL");
1104        assert_ne!(ai.status() & 1, 0, "FULL mirror bit 0");
1105        assert_ne!(ai.status() & (1 << 25), 0, "ENABLED");
1106    }
1107
1108    /// **A sample due exactly at `now` is emitted on that call, not the next one.**
1109    ///
1110    /// [`Audio::tick`] returns early — before computing the DAC period — when no
1111    /// sample is due, which is what keeps a 64-bit divide off the hot path. The
1112    /// boundary of that early-out is `now < next_sample_tick`, and an off-by-one
1113    /// there defers every sample by one RCP step: audio that is *correct but late*,
1114    /// with no wrong sample anywhere.
1115    ///
1116    /// Nothing else in this suite can see that. Mutating the test to `<=` leaves the
1117    /// whole workspace green, because the other AI tests advance `now` in strides of
1118    /// a full period or longer and so never land on the boundary. The gross failure
1119    /// (never emitting at all) is caught seven times over; this one-tick case was
1120    /// caught zero times, which is why it is pinned here rather than assumed.
1121    ///
1122    /// `next_sample_tick` is read directly instead of recomputing the anchor from
1123    /// `period_ticks`, so the test asserts against where the DAC actually is rather
1124    /// than against a second copy of the formula it is meant to check. The schedule
1125    /// comes from `write_reg` — enqueuing a transfer sets `next_sample_tick` — so no
1126    /// priming tick is needed and none is issued; an earlier version called `tick`
1127    /// first and claimed it anchored the schedule, which it did not.
1128    #[test]
1129    fn a_sample_due_exactly_now_is_emitted_on_this_call() {
1130        let mut ai = programmed();
1131        let mut bus = TestBus::new(0x1_0000);
1132        bus.write_word(0x100, 0x0001_0002);
1133        bus.write_word(0x104, 0x0003_0004);
1134        ai.write_reg(0, 0x100);
1135        ai.write_reg(1, 8); // 8 bytes = 2 sample-pairs
1136
1137        let due = ai.next_sample_tick;
1138        assert!(
1139            due > 1,
1140            "enqueuing a transfer must schedule the next sample"
1141        );
1142
1143        ai.tick(due - 1, &mut bus);
1144        assert!(
1145            ai.drain().is_empty(),
1146            "no sample is due one tick before the boundary"
1147        );
1148
1149        ai.tick(due, &mut bus);
1150        assert_eq!(
1151            ai.drain(),
1152            [StereoSample { left: 1, right: 2 }],
1153            "the sample due exactly at `now` must be emitted on this call"
1154        );
1155    }
1156
1157    #[test]
1158    fn plays_a_buffer_and_drains_it() {
1159        let mut ai = programmed();
1160        let mut bus = TestBus::new(0x1_0000);
1161        // 4 stereo words at 0x100.
1162        for i in 0..4u32 {
1163            bus.write_word(0x100 + i * 4, 0x0001_0002u32.wrapping_add(i));
1164        }
1165        ai.write_reg(0, 0x100);
1166        ai.write_reg(1, 16); // 16 bytes = 4 sample-pairs
1167        // Advance exactly 4 sample periods (the anchor puts sample 1 at +period,
1168        // so ticks period..=4*period emit 4 samples before the DAC would decay).
1169        let period = ai.period_ticks();
1170        ai.tick(period * 4, &mut bus);
1171        let out = ai.drain();
1172        assert_eq!(out.len(), 4, "exactly the 4 buffered samples played");
1173        assert_eq!(out[0], StereoSample { left: 1, right: 2 });
1174        assert_eq!(out[3], StereoSample { left: 1, right: 5 });
1175    }
1176
1177    #[test]
1178    fn second_buffer_promotes_and_raises_irq_on_start() {
1179        let mut ai = programmed();
1180        let mut bus = TestBus::new(0x1_0000);
1181        // 8-byte (two-pair) buffers — AI_LENGTH granularity is 8 bytes (& ~7).
1182        bus.write_word(0x100, 0x1111_2222);
1183        bus.write_word(0x104, 0x1111_3333);
1184        bus.write_word(0x200, 0x3333_4444);
1185        bus.write_word(0x204, 0x3333_5555);
1186        ai.write_reg(0, 0x100);
1187        ai.write_reg(1, 8); // buffer 1
1188        ai.write_reg(0, 0x200);
1189        ai.write_reg(1, 8); // buffer 2 (queued, no IRQ yet)
1190        let period = ai.period_ticks();
1191        ai.tick(period * 6, &mut bus);
1192        let out = ai.drain();
1193        assert_eq!(
1194            out[0],
1195            StereoSample {
1196                left: 0x1111,
1197                right: 0x2222
1198            }
1199        );
1200        assert_eq!(
1201            out[2],
1202            StereoSample {
1203                left: 0x3333,
1204                right: 0x4444
1205            }
1206        );
1207        assert_eq!(bus.irqs, 1, "promotion of buffer 2 raised exactly one IRQ");
1208    }
1209
1210    /// **The delayed-carry hardware bug.** A transfer whose final sample ends
1211    /// exactly on an `0x2000` page boundary makes the AI add `0x2000` to the
1212    /// *next* buffer's address. This test fails if the bug is "corrected".
1213    #[test]
1214    fn delayed_carry_bug_bumps_the_next_buffer() {
1215        let mut ai = programmed();
1216        let mut bus = TestBus::new(0x1_0000);
1217        // Buffer 1: two pairs (8 bytes) ending exactly on the 0x2000 boundary —
1218        // the final read at 0x1FFC advances the low 13 bits 0x1FFC → 0x2000,
1219        // wrapping to 0 and arming the deferred carry.
1220        bus.write_word(0x1FF8, 0xAAAA_BBBB);
1221        bus.write_word(0x1FFC, 0xAAAA_CCCC);
1222        ai.write_reg(0, 0x1FF8);
1223        ai.write_reg(1, 8);
1224        // Buffer 2 is programmed at 0x0100, but the delayed carry adds 0x2000,
1225        // so playback reads from 0x2100 instead.
1226        bus.write_word(0x0100, 0x0000_0000); // what a correct AI would read
1227        bus.write_word(0x2100, 0xCCCC_DDDD); // what the buggy AI actually reads
1228        ai.write_reg(0, 0x0100);
1229        ai.write_reg(1, 8);
1230        let period = ai.period_ticks();
1231        ai.tick(period * 8, &mut bus);
1232        let out = ai.drain();
1233        assert_eq!(
1234            out[1],
1235            StereoSample {
1236                left: 0xAAAAu16 as i16,
1237                right: 0xCCCCu16 as i16
1238            }
1239        );
1240        assert_eq!(
1241            out[2],
1242            StereoSample {
1243                left: 0xCCCCu16 as i16,
1244                right: 0xDDDDu16 as i16
1245            },
1246            "the delayed carry bumped buffer 2's address by 0x2000"
1247        );
1248    }
1249
1250    #[test]
1251    fn underrun_is_observable_and_decays() {
1252        let mut ai = programmed();
1253        let mut bus = TestBus::new(0x1_0000);
1254        bus.write_word(0x100, 0x4000_4000);
1255        bus.write_word(0x104, 0x4000_4000);
1256        ai.write_reg(0, 0x100);
1257        ai.write_reg(1, 8); // two pairs, then starvation
1258        let period = ai.period_ticks();
1259        ai.tick(period * 6, &mut bus);
1260        assert_eq!(ai.underruns(), 1, "the starvation is counted");
1261        let out = ai.drain();
1262        assert_eq!(
1263            out[0],
1264            StereoSample {
1265                left: 0x4000,
1266                right: 0x4000
1267            }
1268        );
1269        // Once starved, samples decay toward zero from the last held value.
1270        assert!(out.len() > 2 && out[2].left < 0x4000 && out[2].left > 0);
1271    }
1272
1273    #[test]
1274    fn emission_is_deterministic() {
1275        let run = || {
1276            let mut ai = programmed();
1277            let mut bus = TestBus::new(0x1_0000);
1278            for i in 0..8u32 {
1279                bus.write_word(0x100 + i * 4, 0xDEAD_0000u32.wrapping_add(i));
1280            }
1281            ai.write_reg(0, 0x100);
1282            ai.write_reg(1, 32);
1283            ai.tick(ai.period_ticks() * 16, &mut bus);
1284            ai.drain()
1285        };
1286        assert_eq!(run(), run(), "same input → byte-identical sample stream");
1287    }
1288
1289    #[test]
1290    fn version_is_non_empty() {
1291        assert!(!version().is_empty());
1292    }
1293}