ZoomMultistompZDL

Airwindows 1:1 Port Roadmap

Last updated: 2026-05-18

This is the practical plan for getting from safe ABI probes to real 1:1 Airwindows ports.

Current status update: ctx[3] is no longer just a lead. Hardware probes, the working StereoChorus release, and T9InitOnly show it is a usable per-instance descriptor arena for large state. The no-divide full ToTape9 kernel now loads and runs on the test MS-70CDR. VerbTiny and Galactic are now reverb candidates built on the same strategy. The next blockers are parameter/default lifecycle behavior, hardware reload behavior, and source-equivalence measurement.

What the TI PDFs Tell Us

The PDFs in docs/ explain the C6000 architecture and toolchain, but not Zoom’s private effect-host ABI. They do tell us what not to assume:

So the manuals validate our conservative build rules. They do not answer where stock Zoom effects keep large delay/reverb state.

New Stock-Effect Clue

Disassembling stock stateful effects shows that they use more of the ctx structure than our custom effects currently map.

Observed audio-entry reads:

Stock effect Early ctx fields used in audio
MS-70CDR_STCHO.ZDL / CHORUS.ZDL ctx[1], ctx[2], ctx[3], ctx[5], ctx[11], ctx[12], ctx[13], ctx[14]
MS-70CDR_DELAY.ZDL ctx[1], ctx[2], ctx[3], ctx[5], ctx[11], ctx[12]
MS-70CDR_HALL.ZDL ctx[1], ctx[2], ctx[3], ctx[5], ctx[11], ctx[12]
MS-70CDR_TAPEECHO.ZDL same family of fields, plus dense reads/writes through a structure loaded from ctx[1]/ctx[2]

That was the strongest lead, and the ctx[3] part is now confirmed enough for custom ZDLs: ctx[3][0..2] describe a host-managed large buffer. ctx[2] small state and ctx[13]/ctx[14] still deserve mapping, but they no longer block every large-state Airwindows attempt.

Required Milestones

1. Keep mapping the full audio ctx

Build tiny hardware probes that read one candidate field at a time and encode the result audibly or visibly. Do not dereference unknown pointers first.

Probe order:

  1. Log or sonify raw values of ctx[0..15].
  2. For pointer-looking values, test read-only dereferences of word 0.
  3. Test small writes only to fields proven writable by stock disassembly.
  4. Compare values across effect load, bypass, parameter edit, preset switch, and two instances of the same effect.

The two-instance test matters: Airwindows plugin state must be per effect instance, not one static global shared by every slot.

2. Understand stock state descriptors

Use stock Delay, STCHO, Hall, ModRev, and TapeEcho as the corpus. For each:

  1. Extract ELF with build.zdl.Zdl.
  2. Disassemble .audio and _init.
  3. Identify every access rooted in ctx[2], ctx[3], ctx[13], ctx[14], and parameter-table structures.
  4. Classify each as scalar state, circular-buffer pointer, buffer length, wrap limit, coefficient, or host callback.
  5. Diff effects to find common layouts.

Goal: a documented struct map, not just “some pointer probably works”.

3. Keep the proven ctx[3] state path load-safe

StereoChorus proves the large-buffer route can work. For each new stateful plugin:

  1. Validate ctx[3][0..2] before use.
  2. Keep .fardata at 0 or tiny.
  3. Initialize large memory lazily across audio callbacks.
  4. Prove two instances do not share state.
  5. Prove bypass and preset switching reset or preserve state in a documented way.

4. Build a desktop Airwindows equivalence harness

For each target plugin:

  1. Vendor or reference the exact upstream Airwindows source path/commit.
  2. Extract the DSP into a plain C kernel with an explicit state struct.
  3. Run upstream Airwindows and the C-shaped kernel on identical impulses, tones, silence, and randomized blocks.
  4. Compare output within a defined tolerance.
  5. Freeze parameter laws and defaults in manifest.json.

This separates “does the Airwindows port match source?” from “does the pedal loader run it?”

5. Port the exact kernel onto the proven state strategy

For StereoChorus, the target state is:

At 44.1 kHz on the pedal, cycleEnd should be 1, so the high-sample-rate undersampling reference path can be mechanically retained but should stay inactive unless the host rate is ever proven different.

Immediate Work Items

  1. Verify the zero-as-unmaterialized read-only parameter fallback and corrected LineSel raw 0..0.14 scaling, then document whether first-touch Bias/Output and zero-output startup are fixed. Keep init materialization in isolated probes; the first ToTape9 object-defined init shim crashed on boot.
  2. Test ToTape9 saved preset and preset-switch behavior with the read-only fallback path.
  3. Build an isolated tiny-DSP page 2/3 parameter probe using synthesized LineSel-cloned edit handlers, so params[7..13] updates are proven separately from the ToTape9 kernel.
  4. Add a desktop equivalence harness before calling ToTape9 1:1.
  5. Hardware-test VerbTiny.ZDL and Galactic.ZDL for load, unbypass, page 2 controls, reload, and duplicate-instance state isolation.
  6. Continue mapping ctx[2], ctx[13], and ctx[14] from stock delay, modulation, and tape effects.
  7. Record every hardware result in docs/STATE-ABI-PROGRESS.md before moving the claim into release docs.

Stop Conditions

Do not call a build a 1:1 Airwindows port if: