# P23 SineNEON — Audit **Status:** math verified; source correct; reference oracle on disk; font-table emission unverified. **Auditor:** Flux (math), Atlas (registers — confirmed), Lyra (reference oracle + stress). **Date:** 2026-05-29 --- ## Lithos source (`pfuncs/p23-sineneon.ls`) ``` \ p23-sineneon.ls -- Sine NEON (4v via recurrence) \ Font blob picks feedback path. sineneon ⇌ ∿ ``` One glyph. The source asserts the mathematical identity "this is sine"; the DSP font for `arm64-neon` is responsible for emitting the 4-voice coupled-recurrence blob when it sees `∿` in V.4S context. ## Hand-asm (`sixth/qv/lib/p-functions.fs`, `emit-p23`, lines 1249–1266) 10 instructions, 40 bytes. ``` LDR Q11, [X9, #0] ; 4× c1 LDR Q12, [X9, #16] ; 4× amplitude LDR Q13, [X9, #32] ; 4× y_{n-1} LDR Q14, [X9, #48] ; 4× y_{n-2} FMUL V0.4S, V11.4S, V13.4S ; c1 · y_{n-1} FSUB V0.4S, V0.4S, V14.4S ; y_new = c1·y_{n-1} − y_{n-2} STR Q13, [X9, #48] ; y_{n-2} ← old y_{n-1} STR Q0, [X9, #32] ; y_{n-1} ← y_new FMUL V10.4S, V0.4S, V12.4S ; output = y_new × amp RET ``` ## Math **Recurrence sine** (Chebyshev recurrence with `c1 = 2cos θ`): ``` y[n] = c1 · y[n−1] − y[n−2], c1 = 2·cos(2π·f / sr) ``` Initial conditions to produce `y[n] = sin(n · ω)` for ω = 2π·f/sr: ``` y[−1] = sin(−ω) = −sin(ω) y[−2] = sin(−2ω) ``` Output per voice: `output[n] = y[n] · amplitude`. **Why it's correct:** the Chebyshev identity `2cos(θ)·sin(nθ) − sin((n−1)θ) = sin((n+1)θ)` makes the recurrence produce sine exactly in the algebraic sense. Error is IEEE-754 accumulation only — no Taylor truncation. **Stability:** `|c1| ≤ 2`, so the recurrence's poles sit exactly on the unit circle. Marginally stable. Over long durations, float roundoff drifts amplitude and phase. The `|state| > ±32` D-reg saturation guard in the audio callback catches runaway; periodic state reset (note-off, slot deactivation) keeps drift bounded. **Stateless across notes; stateful across samples:** `y_{n-1}` and `y_{n-2}` persist in context (`[X9, #32]` and `[X9, #48]`). Note-on writes initial conditions; the recurrence does the rest. ## Register/state trace - **Params (constant per buffer):** `Q11` ← context[+0] = 4× c1; `Q12` ← context[+16] = 4× amplitude. - **Persistent state across samples:** `Q13` ← context[+32] = 4× y_{n−1}; `Q14` ← context[+48] = 4× y_{n−2}. Written back each sample. - **Signal in:** none — P23 is a generator. - **Signal out:** `V10.4S` = 4-voice sine (each lane one voice). - **Scratch:** `V0.4S` (holds y_new before output FMUL). - **Persistent D-regs across callbacks:** state lives in context memory, not Q16–Q31; this P-function doesn't use the D-reg persistent slots. State survival across callback boundaries is by virtue of `[X9, #32/#48]` being the same memory. ## Glyph chain → DSP font opcodes The source is `∿` alone. For the font to emit the 10-instruction hand-asm, the `∿` entry in `arm64-dsp-neon-font` (which does not yet exist as a complete table — Atlas's audit on the font side) must encode: | Operation | ARM64 | |---|---| | load c1 | `LDR Q11, [X9, #0]` | | load amp | `LDR Q12, [X9, #16]` | | load y_{n−1} | `LDR Q13, [X9, #32]` | | load y_{n−2} | `LDR Q14, [X9, #48]` | | multiply | `FMUL V0.4S, V11, V13` | | subtract | `FSUB V0.4S, V0, V14` | | store y_{n−2} | `STR Q13, [X9, #48]` | | store y_{n−1} | `STR Q0, [X9, #32]` | | scale output | `FMUL V10.4S, V0, V12` | Whether this fits as a single ngram entry (`∿` in NEON context → 9-instruction blob) or as a small composition (`→→→ →→→ →→→ →→→ ** -- ←←← ←←← **` with register conventions overriding the bare V2/V3 default) is a font-design question for Atlas. The math identity is correct as-is; the question is whether the font can be made to emit this for `∿` cleanly. **NEON-PORT.md proposed expansion** `→ → ← * → - ← → *` does **not** match the instruction order. Stores happen between math ops (after FSUB, before output FMUL), not at chain endpoints. That expansion should be revised or dropped in favor of the single-glyph identity `∿`. ## Finding **Source is correct.** `sineneon ⇌ ∿` accurately expresses the mathematical identity. No source change needed. **Blocker is font-table.** The arm64-NEON font must contain an entry (or composition resolution) for `∿` in 4-voice context that emits the 10-instruction recurrence-sine sequence with state in `[X9, #32/#48]`. This is the same blocker class as the other NEON P-functions: the font needs NEON ngram coverage. **Verification needed once the font emits:** byte-identical match between `arm64-dsp-neon-font(∿)` and the hand-asm at `sixth/qv/lib/p-functions.fs:1249–1266`. ## Stress / sonic notes (Lyra) **Reference oracle on disk.** `pfuncs/audit/refs/p23/` holds f64-evaluated streams at f = 440 Hz, amp = 1.0, sr = 48 000, written as f32 LE: - `ref_truth_1s.f32` — closed-form `sin(2π·440·n/sr)`. - `ref_recurrence_f64_1s.f32` — Chebyshev recurrence in f64. - 30 s versions of both, for long-sustain drift exposure. - `analysis.md` — what the streams contain, how to read them, what the diff metrics are. - `generate.py` — regenerates from the equation; reproducible. **Drift measured directly.** Generating both streams in f64 then storing as f32: - 1 s: peak |truth − recurrence| = **2.3e-11** (bit-identical at f32 precision). - 30 s: peak |truth − recurrence| = **6.0e-08** (~0.5 f32 ULP — marginal-stability drift becomes barely visible). This pins what to expect from the NEON blob: at 1 s, output should be bit-identical to `ref_truth_1s.f32`; at 30 s, output should drift in the same direction as `ref_recurrence_f64_30s.f32`, and **not faster**. Faster drift = FMA fused in a different order than the f64 recurrence, losing precision. **Compare tool.** `pfuncs/audit/refs/lib/compare.py:diff_streams(a, b)` returns peak/rms sample error, peak/rms in dB, Hilbert-envelope error, and per-octave-band spectral error in dB. Ready to consume `ref_handasm.f32` and `ref_lithos.f32` once the runner produces them. **Risks at port — what to listen for / measure for:** - **Float drift over long sustains** — confirmed quantitatively above. For 30 s tests use `ref_recurrence_f64_30s.f32` as the comparison target rather than the closed form; the recurrence-in-f64 is the more faithful prediction of what the blob will produce. - **Initial conditions sensitivity** — y_{−1} and y_{−2} are written at note-on by the host. Wrong values → wrong amplitude or phase from sample 0. Outside the port's scope, but a bug that would show up as a sample-0 mismatch in the diff. - **State reset on deactivation** — voice freed without zeroing state slots means the next note inherits one sample of the previous voice's recurrence — audible as a transient click if amplitudes differ. Detectable: zero-input sustain after a noteOff should produce zero output within one sample; if it doesn't, state isn't being cleared. - **Spectral cleanliness** — at 440 Hz the only spectral content above the noise floor should be the 440 Hz line itself. Any non-DC, non-440 peak above −120 dB indicates denormal-flush artifacts, accumulator overflow, or store-order bugs. ## Next step 1. **Atlas:** font-side — design or locate the NEON font entry for `∿` that emits the 9-instruction blob; verify byte-identity. Audit runner produces `ref_handasm.f32` and `ref_lithos.f32` at f = 440 Hz, amp = 1.0, sr = 48 000, durations 1 s and 30 s, into `pfuncs/audit/refs/p23/`. 2. **Lyra:** ✓ reference oracle on disk at `pfuncs/audit/refs/p23/`. Will run `diff_streams` against the blob outputs once the runner produces them, and record results in this audit page. 3. **Flux:** moving to P28 SubMixNEON. ## Verification status - [x] Source `.ls` read. - [x] Hand-asm read and decoded. - [x] Math recovered. - [x] Register trace. - [x] Glyph-chain analysis vs hand-asm. - [x] Reference oracle on disk (f64 truth + f64 recurrence, 1 s and 30 s). - [x] Drift behavior characterized quantitatively. - [ ] Compile through Lithos compiler — pending NEON font extension. - [ ] Byte-identical diff — pending compile. - [ ] Sonic A/B vs reference — pending blob output from runner.