Shared kernel — per-frame control flow

The 60-word common header (../disasm/kernel.dsm) is uploaded once at boot and runs every sample frame. It is the backbone every effect shares: it sets up each unit’s data pointers, CALLs the two effect bodies in turn, and closes with the output epilogue. This control flow is PROVEN BY CONSTRUCTION (notes/kn5000-dsp-headerdecode.md): the header loads the same three pointer registers twice, so a body must run and return between the two loads.

flowchart TD
    N0["Fs edge &rarr; hardware PC restart (PC := 0)<br/>one pass per sample @ 44,100 Hz"]
    N1["Header preamble, I-RAM 0..48<br/>input stage, LFOs, mixes; load unit-0 pointers<br/>0x821/0x827/0x825 &larr; 0x70/0x6C/0x25"]
    N0 --> N1
    N2["CALL unit-0 body @ I-RAM 84<br/>(END-OF-BLOCK word 49, unit tag 0x0E)"]
    N1 --> N2
    N3["unit-0 effect body runs<br/>straight-line, HAND-UNROLLED (no loop, no branch)"]
    N2 --> N3
    N4["RETURN to I-RAM 50"]
    N3 --> N4
    N5["unit-1 pointer setup, I-RAM 50..58<br/>0x821/0x827/0x825 &larr; 0x50/0x64/0x25"]
    N4 --> N5
    N6["CALL unit-1 body @ I-RAM 200<br/>(END-OF-BLOCK word 59, unit tag 0x0F)"]
    N5 --> N6
    N7["unit-1 effect body runs (the reverb tank)<br/>straight-line, HAND-UNROLLED"]
    N6 --> N7
    N8["RETURN to I-RAM 60"]
    N7 --> N8
    N9["Frame epilogue, I-RAM 60..82<br/>effect-return / wet-level words @ 64 &amp; 71 (host-patched)<br/>output stage"]
    N8 --> N9
    N10["Wait for Fs @ I-RAM 82 (C00.A.47.407)<br/>halt until the next sample clock"]
    N9 --> N10
    N11["2-level call/return stack; call &amp; return share one encoding<br/>(class4==1, addr8 = unit tag 0x0E/0x0F)"]
    N9 -.-> N11

    classDef io fill:#e8eef7,stroke:#33475b,stroke-width:1px,color:#111;
    classDef proven fill:#d7f0d7,stroke:#2e7d32,stroke-width:2px,color:#111;
    classDef measured fill:#dbe9fb,stroke:#1565c0,stroke-width:1.5px,color:#111;
    classDef inferred fill:#fdf0d5,stroke:#b8860b,stroke-width:1.5px,color:#111;
    classDef open fill:#eeeeee,stroke:#888,stroke-width:1px,color:#333,stroke-dasharray:5 5;
    classDef ctrl fill:#f3e8fb,stroke:#6a1b9a,stroke-width:1px,color:#111;
    class N0,N1,N2,N4,N5,N6,N8 proven;
    class N3,N7,N10,N11 measured;
    class N9 inferred;

Key facts (all from notes/kn5000-dsp-headerdecode.md):

  • Per-frame hardware PC restart — the Fs-RST/PC-RST pins restart the PC every sample; there is no software frame loop. The last word, C00.A.47.407 at I-RAM 82, waits for the next Fs (that hi12=0xC00 occurs zero times in 2974 body words). (MEASURED)
  • Call & return share one encoding — the transfer rides on the unit tag class4==1 && addr8 &isin; {0x0E, 0x0F}, not on the END-OF-BLOCK bit; the default action after an END-OF-BLOCK word is fall-through. (PROVEN)
  • A 2-level stack — unit 0 is called at word 49 and returns to 50; unit 1 at word 59 returns to 60. Both units are resident at once but run time-multiplexed. (PROVEN)
  • Bodies are branchless, hand-unrolled — no word carries the entry addresses 84/200 and two exhaustive branch-field scans are negative; there is no loop to branch back to. (MEASURED)
  • The epilogue (I-RAM 60..82) is the output stage — the only two words the host patches per effect, I-RAM 64 and 71, carry the unit tags in their default form and a per-slot-invariant lo12 (0x445/0x446): the effect-return / wet-level words. (INFERRED, strong)

Legend and method: README.md. Narrative: the MAME development blog, KN5000 effects-DSP series (Parts 78-84).