# Device-enable flags (SAI / O2 / lambda) — validated **Aug 2026 update:** re-fetched `20188Map2009AIRBOXBONNY.hex` from its real source — it's in `tuneecu.net`'s own custom-map archive (`Twin_Custom_Tune_list.html`, not a forum attachment as previously assumed), not gitignore-lost this time. Re-ran the diff against a freshly downloaded stock `20188Map.hex` and **every claim below reproduced exactly**: same 1428-byte diff (0.36%), same three device-flag bytes, same two trim bytes. The file now lives in this directory (`20188Map2009AIRBOXBONNY.hex`, `20187/88/91/92Map.hex`) rather than resting on memory of a prior session. See `../COMMUNITY_TUNING.md` for the new composability findings (device flags vs. Arrow exhaust delta) this re-verification enabled. ## How they were found Both stock reference maps (20187, 20188) have SAI and O2 **active**, so diffing them can't reveal the delete flags. The confirmation came from a community map that explicitly disables them: `20188Map2009AIRBOXBONNY.hex` — "Bonneville, aftermarket exhaust, mechanical odometer, NO AIR BOX, K&N, British Custom mufflers, **NO SAI, NO O² SENSORS**". Same base as stock 20188, so the diff isolates the deletes. Diff (flat ROM) of that map vs stock 20188 = 0.36%, split into: - the fuel tables (airbox/K&N enrichment — expected), and - a small cluster in the device-config region at **0x53801…0x53819**. ## The flags They are a **byte-boolean array** at flat-ROM `base + fe[33]` (= `0x50000 + 0x3801 = 0x53801`), one byte per device, **1 = enabled, 0 = disabled**. The delete map changed exactly three bytes from 1 → 0: | Flat-ROM offset | Stock | Deleted | Device | |---|---|---|---| | `0x53801` | 1 | 0 | **SAI** (Secondary Air Injection) | | `0x53818` | 1 | 0 | **O2 sensor** | | `0x53819` | 1 | 0 | **O2 sensor (2)** | SAI is the first flag in the array; the two O2 sensors are the last two — consistent with the `Devices` resource order (SAI = index 0; O2 Sensor / O2 Sensor (2)). The three-byte change matching "NO SAI, NO O²" is unambiguous. To disable a device: set its byte to `0`. (The `0x5369C`/`0x536AB` bytes that also changed are idle/open-loop trim that comes with removing the O2 feedback, not device-enable flags.) ## Editing / export The downloaded `.hex` format's integrity is the `dc` stream cipher + the unpack directory; the `caXX` bytes in the header/tail are map-ID metadata, **not** a calibration checksum. So a device toggle = flip the byte in the flat ROM, re-pack to the decoded layout, and `dc`-encode back to `.hex`. (The separate *ECU flash* checksum is computed at flash time and is out of scope for the read/edit tool.) ## Confidence - **SAI = 0x53801: ~90% (high).** Two independent confirmations: the device-name logic in `l.java` `lc()` resolves `Devices[0]=SAI` via `fe[33]` to exactly 0x53801, AND the real NO-SAI map cleared precisely that byte. - **O2 sensors = 0x53818 / 0x53819: ~80% (probable).** The map declares "NO O² SENSORS" (the twin has two) and exactly two adjacent boolean bytes cleared beyond SAI; the 865 has no air-flap, so airbox removal is fuel-only (no flag). Not independently isolated — no single-mod reference map exists, and the exact `x.a()` device-layout branch for this ECU (i27=72) is too deeply nested to trace reliably. ## The real-world caveat (more important than the labels) Correct flags are **not** a good tune by themselves. Disabling O2 forces the ECU **open-loop**: it stops live fuel trim and runs entirely on the base fuel map. Stock maps assume closed-loop trim at idle/cruise, so an O2-delete on an otherwise stock map can run lean/rough. A proper delete pairs flags-off with fuel enrichment (which is exactly why the AIRBOXBONNY reference map also changed the fuel tables). **Recommendation:** flash a complete, known-good delete map matching the hardware (exhaust/filters), not hand-toggled flags on a stock map. The checkbox editor is for understanding/building a map, not a one-click safe delete. ## `0x53822` — the extra flag found on the America dyno tune (Aug 2026) Pushed on identifying this (index 33 past the `0x53801` base, one slot past the two O2 sensors — see `COMMUNITY_TUNING.md`'s original finding on `20184dynoTuneSteveO2-Disable.hex`). Real, bounded progress, not a full resolution: **Found the actual device name list** — `R.array.Devices` in `arrays.xml:3-18`, 15 entries: `SAI, Exhaust Valve, O2 Sensor, 2nd Throttle, Air Flap, EPC, O2 Sensor (2), Purge Valve, Idle Speed Control, Traction Control, Immobilizer, Instruments, ABS, Speed Sensor (non ABS), Race ECU`. This independently confirms and extends the existing SAI(0)/O2 Sensor(23)/O2 Sensor(2)(24) findings above — matches exactly, real data. **Found the mechanism**: `l.java`'s `lc()` (`l.java:5742`) resolves the per-device flat-ROM byte-boolean array from three packed nibble-encoded constants (`x.t`/`x.s`/`x.q` in the decompile referenced by `docs/ROADMAP.md`), looked up against `R.array.Devices` by index. **This confirms flat-ROM byte position and `Devices[]` array index are not the same thing** — SAI (array index 0) sits at byte 0, but O2 Sensor (array index 2) sits at byte 23, O2 Sensor (2) (array index 6) sits at byte 24 — a bike-specific sparse remapping, not a fixed formula. **Where it stops:** the specific per-family constants that would decode byte 33 precisely require tracing which branch of `x.java`'s ~46 assignments to the relevant classifier variable applies to Bonneville/T100 specifically. `docs/ROADMAP.md`'s existing note cites `i27=72` as the anchor for this ECU family from a prior research pass, but that exact comparison doesn't appear in this decompile snapshot — likely version drift (this decompile's changelog shows APK updates through v6.4.36, June 2026; the `i27=72` note may predate that). Didn't find a reliable replacement anchor without a much larger, low-confidence search across all 46 assignment sites. **Best-supported guess, reasoning not proof:** narrowing the 12 unassigned `Devices[]` names by plausibility for a 2010 mechanical-odo Bonneville/ America-class twin — **Purge Valve** (this project's own `system1.html` research already documents a "Canister Purge Valve, California models only" on this ECU family — a real, independently-corroborated candidate), **Idle Speed Control** (the ISC stepper motor genuinely exists on this engine), and **Immobilizer** (period-correct fuel-injected Triumphs commonly used transponder immobilizers) are the most plausible of the 12; **Exhaust Valve**, **2nd Throttle**, **ABS**, **Traction Control**, **Race ECU** are implausible for this specific bike/era and can likely be ruled out. Not confirmed — a genuine guess ranked by plausibility, not a finding. **Update (Aug 2026) — independently verified, raises confidence further:** checked `tests.html` in this project's own site mirror directly and found a distinct, real testable menu entry: **"Purge Control Valve (Only bikes with charcoal canister) — Activate the purge valve, listen for a very quiet noise,"** listed right alongside its own separate "SAI" test entry. This confirms Purge Valve is a real, independent device on this ECU family with its own dedicated test/enable path, not a guess — genuinely strengthens (does not yet prove) the byte-33 hypothesis. Also confirms from the same list: "Air Flap (675 Daytona)" is explicitly Daytona-675-only, matching this document's elimination reasoning above. (Surfaced by an external research pass, then independently confirmed here directly against this project's own source rather than trusted secondhand — see `../EXTERNAL_RESEARCH_NOTES.md`.) **What would actually resolve this:** a second real-world reference map where the specific device at byte 33 changes state with a documented description (the same technique that resolved SAI/O2 originally) would settle it definitively without needing the `x.t`/`x.s`/`x.q` trace at all. ## Open question raised by community research (unresolved) Multiple forum threads describe TuneECU's own SAI/O2 checkboxes (the same UI these bytes drive) as **only suppressing the fault-code/CEL logic**, not actually forcing open-loop fueling — see `../COMMUNITY_TUNING.md`. That's in tension with "disabling O2 forces open-loop" above. Possible reconciliation: the three device-enable bytes gate DTC logic only, and the *fueling* behavior change lives in the other bytes the real delete-map diff also touched (`0x5369C`, `0x536AB`) or in the lambda-target curve (`fe[2]`/`0x52260`, `TABLES.md`). Not yet isolated — needs a second independent delete-map diff to separate device flags from fuel/trim bytes.