Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions changelog/2026-10-25-octatrack-sidechain-0-2-0-base.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Improvement: Octatrack base (USB, beta) and SDK for Sidechain Compressor 0.2.0.
- A module's DSP section can carry a second table, `ptable2`, placed straight after its `ptable` and loaded by its own `$fab2e0` literal, as upstream octabam's builder places it. This is supported in the vendored octabam schema and builder, the resident DSP engine and the hooked DSP packages. A `$fab2e0` literal left unfilled is refused. Sidechain Compressor needs it: 0.2.0's first build took the key filters' table address from a register the gain store had already moved, which gave silence, level jumps and squeals on KBAS and KWID (the author's MKI, MW_SC020).
- An in-place effect page (the MWPP record) may name up to 16 of the module's own routines, up from 8. Sidechain's page names 9: MON's formatter, switch and step handler, KEY's formatter, switch and step handler, KBAS's and KWID's formatters and KWID's clip.
- The power-up check hides slots 7 to 11 of a side-chain COMPRESSOR from stock's validator, which judges them against stock's value counts. 0.2.0 stores MON as 2 (OFF) or 3 (ON), and stock's count of 2 would refuse the whole bank.
- The key bus tap is one pass: 25 executed instructions a sample for a core's four tracks, down from 41. Its charge stays at the 51 cycles measured on the old tap until the new one is measured.
Checked: the resident-engine, controller, board, power-up and Sidechain tests; native and browser images of every Sidechain selection match (`module:verify`); no pair newly refused.
25 changes: 25 additions & 0 deletions docs/verification/2026-10-26-octatrack-sidechain-0-2-0-base.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Octatrack beta base and SDK for Sidechain Compressor 0.2.0 — 26 October 2026

Owner review. The module's own results are in `sdk/octabam/modules/sidechain-compressor/TESTING.md`.

**What changes.**
- **`DspSection.ptable2`** in the vendored octabam schema and builder (`tools/remix/schema.py`, `tools/build/build_bus.py`), the resident DSP engine (`resident_dsp.py`) and the hooked DSP packages (`scripts/build-module-packages.py`).
- A second table block is placed straight after the `ptable`, with its own `$fab2e0` literal, as upstream octabam places it.
- A `ptable2` without exactly one literal, or a literal without a `ptable2`, is refused.
- Sidechain Compressor 0.2.0 loads its key-filter table this way. 0.2.0's first build took the table's address from a register the gain store had already moved, and that broke KBAS and KWID on the author's MKI (`MW_SC020`).
- **The MWPP page record** may name 16 of the module's own routines, up from 8 (`controller_board.c`, `stock_hook_controllers.page_record`). Sidechain's page names 9.
- **The power-up check** (`machine_registry.c` `sidechain()`) shows stock's validator its own COMPRESSOR values in slots 7-11. MON now stores 2 / 3, and stock's value count of 2 for slot 7 would refuse the bank.
- **The key bus tap is one pass**, 25 executed instructions a sample (was 41). `dsp-sites.json` keeps the 51-cycle hardware charge measured on the old tap.

**How it was checked.**
- **Builder change.** `sdk/infrastructure-verification/ptable2-builder.json` records 128 selections (the floating-cave record's), built natively with the base commit's builder files and with these, both with the base commit's Sidechain folder: 100 identical images and 28 identical refusals.
- **Native against browser.** `module:verify sidechain-compressor` covers 154 selections:
- 66 images match (24 identical outright, 42 outside the platform writes).
- 88 refusals match.
- 0 mismatches.
- It caught the hooked package dropping the `ptable2` before the fix.
- **Pairs.** No pair is newly refused. The refused list (10) is main's.
- **Tests.** The resident engines with the real `dsp_asm` and dsp56300, `test_machine_registry.c` (MON 0-3, slot 7 shown as stock's 1, out-of-range values and other effects reach stock as they are), the controller board, the stock-hook controllers and the infrastructure regressions.
- **Base.** Regenerated with `--beta --write`. Analog BD's machine recipe was recaptured, and only its `imageSha256` moved. The base-bound packages were rebound. The strict stock-free scans pass (longest run 11; no 16-byte run).

**Not tested.** The USB beta base on a unit, including a power cycle with a MON 2/3 bank, which `test_machine_registry.c` covers on the host.
2 changes: 1 addition & 1 deletion public/licenses/THIRD_PARTY_NOTICES.html
Original file line number Diff line number Diff line change
Expand Up @@ -2906,7 +2906,7 @@

MIT License

Copyright (c) 2026 Zac-Kyoti and the OT Kyoti FW contributors
Copyright (c) 2026 Zac Kyoti and the OT Kyoti FW contributors

WHAT THIS LICENSE COVERS

Expand Down
2 changes: 1 addition & 1 deletion public/licenses/THIRD_PARTY_NOTICES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2905,7 +2905,7 @@ Notice source: sdk/octabam/modules/sidechain-compressor/upstream/LICENSE

MIT License

Copyright (c) 2026 Zac-Kyoti and the OT Kyoti FW contributors
Copyright (c) 2026 Zac Kyoti and the OT Kyoti FW contributors

WHAT THIS LICENSE COVERS

Expand Down
Binary file modified public/octatrack-modules/analog-bassdrum/0.1.5-experimental.mwrm
Binary file not shown.
Binary file modified public/octatrack-modules/output-matrix/0.1.1-experimental.mwrm
Binary file not shown.
Binary file not shown.
Binary file not shown.
15 changes: 11 additions & 4 deletions scripts/build-module-packages.py
Original file line number Diff line number Diff line change
Expand Up @@ -375,12 +375,19 @@ def package(module, text):
for tag in sorted(module.dsp.payloads):
source = module.dsp.source_for(tag, text)
validate_source(source)
words, symbols = native.assemble_syms(source.replace(native.PTABLE_LITERAL, '$0'), len(module.dsp.ptable), label=module.key)
words = list(module.dsp.ptable) + words
# The ptable2 (its own $fab2e0 literal) follows the ptable, as the native build places it.
tables = list(module.dsp.ptable) + list(getattr(module.dsp, 'ptable2', ()) or ())
def at(base):
text = source.replace(native.PTABLE_LITERAL, '$'+format(base,'x'))
if getattr(module.dsp, 'ptable2', ()): text = text.replace('$fab2e0', '$'+format(base + len(module.dsp.ptable),'x'))
if '$fab2e0' in text: raise ValueError(id + ': a $fab2e0 literal without a ptable2')
return text
words, symbols = native.assemble_syms(at(0), len(tables), label=module.key)
words = tables + words
relocations = None; proofs = []
for base in (0x1000, 0x1400, 0x1801, 0x2407):
fresh, _ = native.assemble_syms(source.replace(native.PTABLE_LITERAL, '$'+format(base,'x')), base + len(module.dsp.ptable), label=module.key)
fresh = list(module.dsp.ptable) + fresh
fresh, _ = native.assemble_syms(at(base), base + len(tables), label=module.key)
fresh = tables + fresh
changed = [i for i, (a,b) in enumerate(zip(words,fresh)) if a != b]
if len(fresh) != len(words) or any(fresh[i] != words[i] + base or not 0 <= words[i] < len(words) for i in changed):
raise ValueError(id + ': unsupported relocation')
Expand Down
2 changes: 1 addition & 1 deletion scripts/verify-octatrack-dsp-loader.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ if (mode === 'dumps') {
const now = await page()
if (!live) return assert.deepEqual(now, stockPage, 'COMPRESSOR\'s page is stock again')
const at = await imageAt(sc), formatter = now.readUInt32BE(24 + 4 + 16)
assert.equal(now.toString('latin1', 0, 24).replace(/\0+/g, ' ').trim(), 'KEY KFLT KGN MON', 'the KEY page names')
assert.equal(now.toString('latin1', 0, 24).replace(/\0+/g, ' ').trim(), 'KEY KGN KBAS KWID', 'the KEY page names')
assert(formatter >= at && formatter < at + sc.image.length, 'the KEY formatter is the package\'s own')
}
const install = async (p, name) => {
Expand Down
2 changes: 1 addition & 1 deletion sdk/catalog.json
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@
},
{
"id": "sidechain-compressor",
"version": "0.1.2-experimental",
"version": "0.2.0-experimental",
"addedAt": "2026-10-05T12:00:00Z"
},
{
Expand Down
164 changes: 164 additions & 0 deletions sdk/imports/sidechain-compressor-b0d49bf.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
{
"schemaVersion": 1,
"repository": "https://github.com/Zac-Kyoti/octatrack-kyoti-fw",
"revision": "b0d49bf32d3fcc2e3ce7e9cec67f4fbb2619e511",
"moduleVersion": "0.2.0-experimental",
"root": "sdk/octabam/modules/sidechain-compressor",
"modules": [
"sidechain-compressor"
],
"files": [
{
"path": "manifest.py",
"repository": "https://github.com/Zac-Kyoti/octatrack-kyoti-fw",
"revision": "b0d49bf32d3fcc2e3ce7e9cec67f4fbb2619e511",
"sourcePath": "modwerk-modules/sidechain-compressor/manifest.py",
"sourceBytes": 12605,
"sourceGitBlob": "4b25454bbfd3ea03812bc9bf01d46f77e72a944a",
"sourceSha256": "62f004df8eb3e727da05430b18785142f6f3de6c1418b42de49b9073d6f17e44",
"vendoredSha256": "62f004df8eb3e727da05430b18785142f6f3de6c1418b42de49b9073d6f17e44"
},
{
"path": "patch_sc_dsp3.asm",
"repository": "https://github.com/Zac-Kyoti/octatrack-kyoti-fw",
"revision": "b0d49bf32d3fcc2e3ce7e9cec67f4fbb2619e511",
"sourcePath": "modwerk-modules/sidechain-compressor/patch_sc_dsp3.asm",
"sourceBytes": 53364,
"sourceGitBlob": "1b33bba89ba1250a06ae29599bc208884778f0e8",
"sourceSha256": "11022dbd84e1fffa71946aa19e8d8f0a51b1f25371c5b6b794066c855012ddf8",
"vendoredSha256": "11022dbd84e1fffa71946aa19e8d8f0a51b1f25371c5b6b794066c855012ddf8"
},
{
"path": "patch_sidechain.s",
"repository": "https://github.com/Zac-Kyoti/octatrack-kyoti-fw",
"revision": "b0d49bf32d3fcc2e3ce7e9cec67f4fbb2619e511",
"sourcePath": "modwerk-modules/sidechain-compressor/patch_sidechain.s",
"sourceBytes": 14900,
"sourceGitBlob": "762ecfa829822738aad09a7d4d19bdee71b0486a",
"sourceSha256": "0cbd52f14497253f067f91f54e9bbeb96176e73bb8a438edb89aaf3d09abd325",
"vendoredSha256": "0cbd52f14497253f067f91f54e9bbeb96176e73bb8a438edb89aaf3d09abd325"
},
{
"path": "ui_switch.s",
"repository": "https://github.com/Zac-Kyoti/octatrack-kyoti-fw",
"revision": "b0d49bf32d3fcc2e3ce7e9cec67f4fbb2619e511",
"sourcePath": "modwerk-modules/sidechain-compressor/ui_switch.s",
"sourceBytes": 8809,
"sourceGitBlob": "b974249331482bfcce71795331b075b4291e9672",
"sourceSha256": "643c8e68a6f914896740406599369c5d6e0112f1b2f388233a2bfcda10b7b4ab",
"vendoredSha256": "643c8e68a6f914896740406599369c5d6e0112f1b2f388233a2bfcda10b7b4ab"
},
{
"path": "sc_tables.py",
"repository": "https://github.com/Zac-Kyoti/octatrack-kyoti-fw",
"revision": "b0d49bf32d3fcc2e3ce7e9cec67f4fbb2619e511",
"sourcePath": "modwerk-modules/sidechain-compressor/sc_tables.py",
"sourceBytes": 4645,
"sourceGitBlob": "e58b2cac6febedfa0fc230a142f6ffd17f44a2de",
"sourceSha256": "cf4bba489c652fca75021f9d81a8aa8ecf0247ce6edc36b01420903c74324054",
"vendoredSha256": "cf4bba489c652fca75021f9d81a8aa8ecf0247ce6edc36b01420903c74324054"
},
{
"path": "sc_keyfilt.py",
"repository": "https://github.com/Zac-Kyoti/octatrack-kyoti-fw",
"revision": "b0d49bf32d3fcc2e3ce7e9cec67f4fbb2619e511",
"sourcePath": "modwerk-modules/sidechain-compressor/sc_keyfilt.py",
"sourceBytes": 8702,
"sourceGitBlob": "52e180ac1957c27acfbd9aa852c5490f0f9fd68f",
"sourceSha256": "8e68216a2ec8ec366c686fd7d6ab0690776b66065de58ab2aa807a5e88438edb",
"vendoredSha256": "8e68216a2ec8ec366c686fd7d6ab0690776b66065de58ab2aa807a5e88438edb"
},
{
"path": "upstream/README.md",
"repository": "https://github.com/Zac-Kyoti/octatrack-kyoti-fw",
"revision": "b0d49bf32d3fcc2e3ce7e9cec67f4fbb2619e511",
"sourcePath": "modwerk-modules/sidechain-compressor/README.md",
"sourceBytes": 1567,
"sourceGitBlob": "3a16cc919b47984b3d3dc25dbb06ad1d01322e3c",
"sourceSha256": "695451e0e2e3485d2510cd116e29c331374bdb75ee0acb1787385ef4a3cc4bdf",
"vendoredSha256": "695451e0e2e3485d2510cd116e29c331374bdb75ee0acb1787385ef4a3cc4bdf"
},
{
"path": "upstream/LICENSE",
"repository": "https://github.com/Zac-Kyoti/octatrack-kyoti-fw",
"revision": "b0d49bf32d3fcc2e3ce7e9cec67f4fbb2619e511",
"sourcePath": "LICENSE",
"sourceBytes": 4451,
"sourceGitBlob": "8cd09176a276a3854ae947ab148b9b327d77e381",
"sourceSha256": "f1f8d5837b6df5d6e5744286e3929883253f1ee754c4f9e0494989f2ef992a05",
"vendoredSha256": "f1f8d5837b6df5d6e5744286e3929883253f1ee754c4f9e0494989f2ef992a05"
},
{
"path": "LICENSE",
"repository": "https://github.com/Zac-Kyoti/octatrack-kyoti-fw",
"revision": "b0d49bf32d3fcc2e3ce7e9cec67f4fbb2619e511",
"sourcePath": "LICENSE",
"sourceBytes": 4451,
"sourceGitBlob": "8cd09176a276a3854ae947ab148b9b327d77e381",
"sourceSha256": "f1f8d5837b6df5d6e5744286e3929883253f1ee754c4f9e0494989f2ef992a05",
"vendoredSha256": "243b2224f86e4b88add7434b5ef3614e5ef951fff50c8fcf3e74dd0c952e8670",
"transforms": [
"The author's MIT licence with one line added at its end: Modwerk's own copyright notice for its import adaptations, verification tools and original documentation/artwork, under the same MIT terms."
]
}
],
"stockGuards": [
{
"kind": "guarded-local-dsp-read",
"payload": "A",
"address": 1191,
"words": 2,
"sha256": "92441a8fc9f1dd20359d87c8342e3d5653d8c853a274358df0cfc0451c57115b",
"note": "Both payloads have the same guarded instruction at their declared sites; recover expectations only from a verified local stock OS."
},
{
"kind": "guarded-local-dsp-read",
"payload": "A",
"address": 6833,
"words": 2,
"sha256": "114aacb95ee45437fff14f2dcd91b67e26d8bc42ef3ba2e9f6ae0cb8be1574fa",
"note": "Both payloads have the same guarded instruction at their declared sites; recover expectations only from a verified local stock OS."
},
{
"kind": "guarded-local-dsp-read",
"payload": "A",
"address": 1294,
"words": 2,
"sha256": "9dad4262559be3a41c85136d50f7d017f161cff9a609d17d21da895cd2137118",
"note": "Both payloads have the same guarded instruction at their declared sites; recover expectations only from a verified local stock OS."
},
{
"address": "0x4000cae8",
"bytes": 6,
"sha256": "9a4b68c7876e6a4b172ba77e67b4305494b67444bae1a8e13c5f6e62ac6e7c2c"
},
{
"address": "0x40003d1c",
"bytes": 6,
"sha256": "9a4b68c7876e6a4b172ba77e67b4305494b67444bae1a8e13c5f6e62ac6e7c2c"
},
{
"payload": "A",
"address": 6820,
"words": 2,
"sha256": "9eb96aa9abbf3d522e54911d6fa634dbcfc8820ad32d593e405bacc87193f305",
"note": "COMPRESSOR init (scinit, 0.2.0); B 0x01864 holds the same two words"
}
],
"excluded": [
"Firmware and stock expectation bytes",
"The author's standalone builders (tools/build_sidechain_compressor.py and its diagnostics), emulator checks (tools/emu_sc_dsp3.py) and tooling outside modwerk-modules/",
"octabam's wrapper of the module (octabam-modules/sidechain-compressor), which is the octabam form of the same sources",
"Generated firmware, runtime images and memory dumps"
],
"notes": [
"Every source file and manifest.py are the author's files byte for byte, from the author's own Modwerk copy of the module (modwerk-modules/sidechain-compressor). The manifest is the Modwerk form: stock expectations as address/length/SHA-256 guards read from the builder's own OS file, no PLACE setting or store.",
"The sources are the author's final SIDECHAIN_COMPRESSOR (the base-width key filter, KEY switch and KWID clip), the same code octabam carries since sambanks/octabam #869, #873 and #875.",
"The stock guards were checked again against the original OS 1.40C MAIN image (SHA-256 164f31224bf61181e3f50e7dec40df9afcae5b16dbf6e4c0d0cc5e986af0a84e) by this version's native build."
],
"status": "Owner review requested: the module changes the beta base's resident engine and controller (resident_dsp.py, stock_hook_controllers.py, dsp-sites.json, machine_registry.c).",
"authorPin": {
"repository": "https://github.com/Zac-Kyoti/octatrack-kyoti-fw",
"revision": "b0d49bf32d3fcc2e3ce7e9cec67f4fbb2619e511"
}
}
Loading
Loading