S.Delay + Flanger/Vibrato/Phaser/S.Delay — HLE + bytecode
S.Delay + Flanger/Vibrato/Phaser/S.Delay — HLE reconstruction and DSP bytecode
DSPHLE selector 0x09 · program image prog66_s_delay_flanger · Intervention-pinned (a panel control was driven and the moving C-RAM cell identified).
shared combo engine: delay chained with a 2nd block (selectors 0x09-0x0c; see the branch for each).
This page pairs the bytecode the effect runs on the NEC µPD6383GF with the high-level reconstruction that makes it audible in MAME. The reconstruction is not the chip’s microcode — it is a textbook DSP block built from the decoded meaning of that microcode, and reading the two together is how the bytecode is understood (and, in time, driven toward a faithful low-level emulation). See also the signal-flow flowchart.
The bytecode below is the source of truth; the HLE reconstruction is not. The HLE is our best current interpretation and may contain mistakes — where the two disagree, the bytecode wins, and a better HLE should be updated here. This page is the permanent archive of the reference HLE: it is kept here even after the code is eventually removed from the MAME sources.
DSP bytecode (reverse-engineered microprogram)
The disassembled image the chip executes for this effect. Source (regenerable):
dsp/disasm/prog66_s_delay_flanger.dsm.
; Reverse-engineered disassembly of the KN5000 effects-DSP microcode
; (NEC uPD6383GF), recovered from the original firmware. The microcode is the
; work of its original authors; this is a disassembly for preservation and
; interoperability, and no claim of copyright is made over the disassembled program.
; KN5000 effects-DSP program -- S.DELAY+FLANGER
; image rep algo 66 | slots 66 | unit 0 (I-RAM load 84)
; family combi | confidence medium | 100 words, 28 class-A multiplies (21 named)
; role: single delay + flanger
; coefficient cursor base 0x00
;
; GENERATED by dsp/tools/gen_dsp_disasm.py -- DO NOT EDIT.
; Put labels/comments in the matching dsp/sym/*.sym; analysis in dsp/algorithms/.
w0 08801308BC ?word 0x08801308BC ; 880.1.30.8BC hi12{ESC ?7 res=080} [external delay-DRAM READ (FORCED, adjudication-round5 sect. 3 -- addr8 bit 6 is the direction field and 0x60 is the WRITE; this REVERSES R1 F1, which bounded the read latency to one repetition when the descriptors need twenty words); this end moves with the user's DELAY (ms) knob, and the delay is READ_CELL - WRITE_CELL; addr8 0x30 also marks the FIRST DRAM access of a body, 37 of 38 distinct images (R3 sect. 6.2). external delay-DRAM access; address = DESCRIPTOR_CELL[k] + G, from the host bank behind pointer ...825 / tag 0x4C (R3, PROVEN BY CONSTRUCTION) -- the k-th class-1 escape word of a body takes the k-th cell of that body's own descriptor block (the IDENTITY map, FORCED in adjudication-round5 sect. 1), so the address is NOT in this word]
w1 00002031CD ld (p),(p)+3
w2 000025940E ld acc,(p)+89
w3 02122A700B ?word 0x02122A700B ; 212.2.A7.00B hi12{ST f98=2 f31=1} [writes mem[ptr] (bit 4); mode 2, so the target IS the pointer]
w4 0092A00200 mac.b c,c+,(p)+0 ; store SUPPRESSED (bit7)
; C-RAM[0x00] (coeff, base 0x00 MEASURED)
w5 00822001C0 mac.b (p),(p)+0
w6 0094A00200 wrap acc,c+ ; acc <- datum(acc) & coef (LFO modulus)
; C-RAM[0x01] (coeff, base 0x00 MEASURED)
w7 0000201447 ?word 0x0000201447 ; 000.2.01.447 hi12{-} [SPECULATIVE (prospective, not measured): SRC 0x11 = ACCB (2nd accumulator)]
w8 0092A00200 mac.b c,c+,(p)+0 ; store SUPPRESSED (bit7)
; C-RAM[0x02] (coeff, base 0x00 MEASURED)
w9 00822001C0 mac.b (p),(p)+0
w10 0094A00200 wrap acc,c+ ; acc <- datum(acc) & coef (LFO modulus)
; C-RAM[0x03] (coeff, base 0x00 MEASURED)
w11 0000209447 ?word 0x0000209447 ; 000.2.09.447 hi12{-} [SPECULATIVE (prospective, not measured): SRC 0x11 = ACCB (2nd accumulator)]
w12 0022200000 mac.b (p)0,(p)+0
w13 00002FE407 ld.st acc,(p)-2
w14 0000A011D5 ld (p),c+,(p)+1
; C-RAM[0x04] (coeff, base 0x00 MEASURED)
; coeff C-RAM[0x04] = filter section cell 0 (role filter, INFERRED)
w15 0202AFF1D5 mac (p),c+,(p)-1
; C-RAM[0x05] (coeff, base 0x00 MEASURED)
; coeff C-RAM[0x05] = filter section cell 1 (role filter, INFERRED)
w16 08801602D9 dly.w dsc[k],p+96
w17 0202A01655 mac ta,c+,(p)+1
; C-RAM[0x06] (coeff, base 0x00 MEASURED)
; coeff C-RAM[0x06] = filter section cell 2 (role filter, INFERRED)
w18 00002FF000 nop
w19 0212200419 mac.ta2 acc,(p)+0 ; mem[p]<-acc, acc=0
w20 088012064B ?word 0x088012064B ; 880.1.20.64B hi12{ESC ?7 res=080} [external delay-DRAM READ (FORCED, adjudication-round5 sect. 3 -- addr8 bit 6 is the direction field and 0x60 is the WRITE; this REVERSES R1 F1, which bounded the read latency to one repetition when the descriptors need twenty words); this end moves with the user's DELAY (ms) knob, and the delay is READ_CELL - WRITE_CELL. external delay-DRAM access; address = DESCRIPTOR_CELL[k] + G, from the host bank behind pointer ...825 / tag 0x4C (R3, PROVEN BY CONSTRUCTION) -- the k-th class-1 escape word of a body takes the k-th cell of that body's own descriptor block (the IDENTITY map, FORCED in adjudication-round5 sect. 1), so the address is NOT in this word]
w21 0000202407 ld.st acc,(p)+2
w22 00002001D5 ld (p),(p)+0
w23 0202AF3415 mac acc,c+,(p)-13
; C-RAM[0x07] (coeff, base 0x00 MEASURED)
; coeff C-RAM[0x07] = op0x66[0] (role mix/tap, INFERRED)
w24 020420D1CD post (p),(p)+13
w25 000020040E ld acc,(p)+0
w26 0212200000 mac.b (p)0,(p)+0 ; mem[p]<-acc, acc=0
w27 00002F8407 ld.st acc,(p)-8
w28 0000A011D5 ld (p),c+,(p)+1
; C-RAM[0x08] (coeff, base 0x00 MEASURED)
; coeff C-RAM[0x08] = filter section cell 4 (role filter, INFERRED)
w29 0202A451D5 mac (p),c+,(p)+69
; C-RAM[0x09] (coeff, base 0x00 MEASURED)
; coeff C-RAM[0x09] = filter section cell 1 (role filter, INFERRED)
w30 0202AC41D5 mac (p),c+,(p)-60
; C-RAM[0x0A] (coeff, base 0x00 MEASURED)
; coeff C-RAM[0x0A] = filter section cell 2 (role filter, INFERRED)
w31 09001602D9 dly.w dsc[k],p+96
w32 0192AF71D5 mac (p),c+,(p)-9 ; store SUPPRESSED (bit7)
; C-RAM[0x0B] (coeff, base 0x00 MEASURED)
; coeff C-RAM[0x0B] = filter section cell 3 (role filter, INFERRED)
w33 0182245000 mac.b (p)0,(p)+69
w34 000020044C ?word 0x000020044C ; 000.2.00.44C hi12{-} [SPECULATIVE (prospective, not measured): SRC 0x11 = ACCB (2nd accumulator); ACT 0x0C = delay READ]
w35 088012040B ?word 0x088012040B ; 880.1.20.40B hi12{ESC ?7 res=080} [external delay-DRAM READ (FORCED, adjudication-round5 sect. 3 -- addr8 bit 6 is the direction field and 0x60 is the WRITE; this REVERSES R1 F1, which bounded the read latency to one repetition when the descriptors need twenty words); this end moves with the user's DELAY (ms) knob, and the delay is READ_CELL - WRITE_CELL. external delay-DRAM access; address = DESCRIPTOR_CELL[k] + G, from the host bank behind pointer ...825 / tag 0x4C (R3, PROVEN BY CONSTRUCTION) -- the k-th class-1 escape word of a body takes the k-th cell of that body's own descriptor block (the IDENTITY map, FORCED in adjudication-round5 sect. 1), so the address is NOT in this word]
w36 0012201655 mac ta,(p)+1 ; mem[p]<-acc, acc=0
w37 01042001D5 post (p),(p)+0
w38 0A00000041 ?word 0x0A00000041 ; A00.0.00.041 hi12{ESC f98=2} [head of a fixed 3-word template (S-5); operands SRC 0x01/ACT 0x01 dark]
w39 08801202C7 dly.r dsc[k],p+32
w40 0102AB94C8 ?word 0x0102AB94C8 ; 102.A.B9.4C8 hi12{f98=1 f31=1} cur+ [gain multiply (same op in phaser all-pass and reverb diffuser)]
; C-RAM[0x0C] (coeff, base 0x00 MEASURED)
; coeff C-RAM[0x0C] = filter section cell 4 (role filter, INFERRED)
w41 0000208407 ld.st acc,(p)+8
w42 00002001D5 ld (p),(p)+0
w43 0202AF6415 mac acc,c+,(p)-10
; C-RAM[0x0D] (coeff, base 0x00 MEASURED)
; coeff C-RAM[0x0D] = op0x66[1] (role mix/tap, INFERRED)
w44 0092200700 ?word 0x0092200700 ; 092.2.00.700 hi12{ST f31=1 ?7 res=080} [SPECULATIVE (prospective, not measured): SRC 0x1C = control/mod source into MAC (100% MAC-consumed; LFO in mod fx, envelope/AGC in dynamics) -- NOT LFO-only: present in 19 non-LFO programs (dsp_datapath_fingerprint)]
w45 0204A061D5 post (p),c+,(p)+6
; C-RAM[0x0E] (coeff, base 0x00 MEASURED)
w46 0182200407 mac.st acc,(p)+0
w47 0040000C63 ?word 0x0040000C63 ; 040.0.00.C63 hi12{?6 res=040} [SPECULATIVE (prospective, not measured): SRC 0x11 = ACCB (2nd accumulator)]
w48 00006184CD ?word 0x00006184CD ; 000.6.18.4CD hi12{-} [table-lookup idiom, class-6 addr8 = table selector (INFERRED)]
w49 00124011CE ?word 0x00124011CE ; 012.4.01.1CE hi12{ST f31=1} [table-lookup idiom, third word (INFERRED)]
w50 01042061CE post (p),(p)+6
w51 0102200000 mac.b (p)0,(p)+0
w52 0000AF5415 ld acc,c+,(p)-11
; C-RAM[0x0F] (coeff, base 0x00 MEASURED)
; coeff C-RAM[0x0F] = op0x66[2] (role mix/tap, INFERRED)
w53 0092200700 ?word 0x0092200700 ; 092.2.00.700 hi12{ST f31=1 ?7 res=080} [SPECULATIVE (prospective, not measured): SRC 0x1C = control/mod source into MAC (100% MAC-consumed; LFO in mod fx, envelope/AGC in dynamics) -- NOT LFO-only: present in 19 non-LFO programs (dsp_datapath_fingerprint)]
w54 0212A0B1D5 mac (p),c+,(p)+11 ; mem[p]<-acc, acc=0
; C-RAM[0x10] (coeff, base 0x00 MEASURED)
w55 0182200407 mac.st acc,(p)+0
w56 0040000C63 ?word 0x0040000C63 ; 040.0.00.C63 hi12{?6 res=040} [SPECULATIVE (prospective, not measured): SRC 0x11 = ACCB (2nd accumulator)]
w57 00006184CD ?word 0x00006184CD ; 000.6.18.4CD hi12{-} [table-lookup idiom, class-6 addr8 = table selector (INFERRED)]
w58 00124011CE ?word 0x00124011CE ; 012.4.01.1CE hi12{ST f31=1} [table-lookup idiom, third word (INFERRED)]
w59 01042F31CE post (p),(p)-13
w60 0102200000 mac.b (p)0,(p)+0
w61 0000AFE415 ld acc,c+,(p)-2
; C-RAM[0x11] (coeff, base 0x00 MEASURED)
; coeff C-RAM[0x11] = op0x66[3] (role mix/tap, INFERRED)
w62 021220A1CD mac (p),(p)+10 ; mem[p]<-acc, acc=0
w63 00002F81CE ld (p),(p)-8
w64 021220B407 mac.st acc,(p)+11 ; mem[p]<-acc, acc=0
w65 0880130000 dly.r dsc[k],p+48
w66 0022200000 mac.b (p)0,(p)+0
w67 00002FE407 ld.st acc,(p)-2
w68 0000A011D5 ld (p),c+,(p)+1
; C-RAM[0x12] (coeff, base 0x00 MEASURED)
w69 0202A001D5 mac (p),c+,(p)+0
; C-RAM[0x13] (coeff, base 0x00 MEASURED)
; coeff C-RAM[0x13] = filter section cell 0 (role filter, INFERRED)
w70 08801602D9 dly.w dsc[k],p+96
w71 0202A00655 mac ta,c+,(p)+0
; C-RAM[0x14] (coeff, base 0x00 MEASURED)
; coeff C-RAM[0x14] = filter section cell 1 (role filter, INFERRED)
w72 0000200000 nop
w73 0212200419 mac.ta2 acc,(p)+0 ; mem[p]<-acc, acc=0
w74 088012064B ?word 0x088012064B ; 880.1.20.64B hi12{ESC ?7 res=080} [external delay-DRAM READ (FORCED, adjudication-round5 sect. 3 -- addr8 bit 6 is the direction field and 0x60 is the WRITE; this REVERSES R1 F1, which bounded the read latency to one repetition when the descriptors need twenty words); this end moves with the user's DELAY (ms) knob, and the delay is READ_CELL - WRITE_CELL. external delay-DRAM access; address = DESCRIPTOR_CELL[k] + G, from the host bank behind pointer ...825 / tag 0x4C (R3, PROVEN BY CONSTRUCTION) -- the k-th class-1 escape word of a body takes the k-th cell of that body's own descriptor block (the IDENTITY map, FORCED in adjudication-round5 sect. 1), so the address is NOT in this word]
w75 0000201407 ld.st acc,(p)+1
w76 00002001D5 ld (p),(p)+0
w77 0202AF3415 mac acc,c+,(p)-13
; C-RAM[0x15] (coeff, base 0x00 MEASURED)
; coeff C-RAM[0x15] = op0x66[4] (role mix/tap, INFERRED)
w78 020420D1CD post (p),(p)+13
w79 000020040E ld acc,(p)+0
w80 0212200000 mac.b (p)0,(p)+0 ; mem[p]<-acc, acc=0
w81 00002F9407 ld.st acc,(p)-7
w82 0000AFF1D5 ld (p),c+,(p)-1
; C-RAM[0x16] (coeff, base 0x00 MEASURED)
; coeff C-RAM[0x16] = filter section cell 3 (role filter, INFERRED)
w83 0202A481D5 mac (p),c+,(p)+72
; C-RAM[0x17] (coeff, base 0x00 MEASURED)
; coeff C-RAM[0x17] = filter section cell 4 (role filter, INFERRED)
w84 0202AB51D5 mac (p),c+,(p)-75
; C-RAM[0x18] (coeff, base 0x00 MEASURED)
; coeff C-RAM[0x18] = filter section cell 2 (role filter, INFERRED)
w85 09001602D9 dly.w dsc[k],p+96
w86 0192A031D5 mac (p),c+,(p)+3 ; store SUPPRESSED (bit7)
; C-RAM[0x19] (coeff, base 0x00 MEASURED)
; coeff C-RAM[0x19] = filter section cell 3 (role filter, INFERRED)
w87 0182248000 mac.b (p)0,(p)+72
w88 000020044C ?word 0x000020044C ; 000.2.00.44C hi12{-} [SPECULATIVE (prospective, not measured): SRC 0x11 = ACCB (2nd accumulator); ACT 0x0C = delay READ]
w89 088012040B ?word 0x088012040B ; 880.1.20.40B hi12{ESC ?7 res=080} [external delay-DRAM READ (FORCED, adjudication-round5 sect. 3 -- addr8 bit 6 is the direction field and 0x60 is the WRITE; this REVERSES R1 F1, which bounded the read latency to one repetition when the descriptors need twenty words); this end moves with the user's DELAY (ms) knob, and the delay is READ_CELL - WRITE_CELL. external delay-DRAM access; address = DESCRIPTOR_CELL[k] + G, from the host bank behind pointer ...825 / tag 0x4C (R3, PROVEN BY CONSTRUCTION) -- the k-th class-1 escape word of a body takes the k-th cell of that body's own descriptor block (the IDENTITY map, FORCED in adjudication-round5 sect. 1), so the address is NOT in this word]
w90 0012201655 mac ta,(p)+1 ; mem[p]<-acc, acc=0
w91 01042001D5 post (p),(p)+0
w92 0A00000041 ?word 0x0A00000041 ; A00.0.00.041 hi12{ESC f98=2} [head of a fixed 3-word template (S-5); operands SRC 0x01/ACT 0x01 dark]
w93 08801202C7 dly.r dsc[k],p+32
w94 0102AB84C8 ?word 0x0102AB84C8 ; 102.A.B8.4C8 hi12{f98=1 f31=1} cur+ [gain multiply (same op in phaser all-pass and reverb diffuser)]
; C-RAM[0x1A] (coeff, base 0x00 MEASURED)
; coeff C-RAM[0x1A] = filter section cell 4 (role filter, INFERRED)
w95 0000207407 ld.st acc,(p)+7
w96 00002001D5 ld (p),(p)+0
w97 0202AEE415 mac acc,c+,(p)-18
; C-RAM[0x1B] (coeff, base 0x00 MEASURED)
; coeff C-RAM[0x1B] = op0x66[5] (role mix/tap, INFERRED)
w98 0880160000 dly.w dsc[k],p+96
w99 060410E000 endblk #0E ; END OF BLOCK -- the last word of a block
HLE reconstruction (MAME, kn5000_tonegen.cpp)
The decode of the bytecode above, rebuilt as audible DSP. Two parts: the parameter refresh
(reads the decoded C-RAM coefficient cells once per update) and the per-sample insert (the
signal processing). Default OFF, behind the DSPHLE research port. Source:
src/mame/matsushita/kn5000_tonegen.cpp.
Parameter refresh (decode the C-RAM coefficients):
const int cmb2 = (dsphle >= 9 && dsphle <= 12) ? dsphle : 0;
int c2_dN = 0;
double c2_gfb = 0.0, c2_gfb2 = 0.0, c2_minc = 0.0, c2_ac = 0.0, c2_ad = 0.0;
if (cmb2)
{
auto q22b = [](u32 v) -> double {
int32_t s = (v & 0x800000) ? int32_t(v) - 0x1000000 : int32_t(v);
return double(s) / 4194304.0; };
const bool bit1 = (dspcfg & 2) != 0;
const double cs = bit1 ? 1.0 : 2.0;
const double n441 = (bit1 ? 1.0 : 2.0) * double(m_dsp1->dsc_read(0x26));
c2_dN = std::clamp(int(n441 * double(STREAM_RATE) / 44100.0 + 0.5), 1, 34999);
auto rate_of = [&](u8 cell) { const double inc = double(m_dsp1->cram_read(cell) & 0xffffff) * cs;
return std::clamp(inc * 44100.0 / 8388608.0, 0.02, 12.0) / double(STREAM_RATE); };
auto fb_of = [&](u8 cell) { return std::clamp(q22b(m_dsp1->cram_read(cell)) * cs * 0.5, -0.95, 0.95); };
if (cmb2 == 9) { c2_gfb = fb_of(0x04); c2_minc = rate_of(0x00); } // +flanger
if (cmb2 == 10) { c2_gfb = fb_of(0x04); c2_minc = rate_of(0x00); } // +vibrato
if (cmb2 == 11) { c2_gfb = fb_of(0x00); c2_minc = rate_of(0x03); // +phaser
c2_ac = std::clamp(q22b(m_dsp1->cram_read(0x0A)) * cs * 0.5, -0.95, 0.95); // manual (centre)
c2_ad = std::clamp(q22b(m_dsp1->cram_read(0x09)) * cs * 0.5, 0.0, 0.40); } // sweep
if (cmb2 == 12) { c2_gfb = fb_of(0x00); c2_gfb2 = fb_of(0x05); } // +s.delay (two)
}
Per-sample insert (the reconstructed signal path):
if (cmb2)
{
const double sl = double(mix_l) / 32768.0, sr = double(mix_r) / 32768.0;
const double dtl = m_dly_l.read(double(c2_dN)), dtr = m_dly_r.read(double(c2_dN));
m_dly_l.write(sl + c2_gfb * dtl);
m_dly_r.write(sr + c2_gfb * dtr);
double dl = 0.5 * sl + 0.5 * dtl, dr = 0.5 * sr + 0.5 * dtr;
double ol = dl, orr = dr;
if (cmb2 == 9) // + FLANGER (feedback delay, resonance ~0.3)
{
m_fl_phase += c2_minc; if (m_fl_phase >= 1.0) m_fl_phase -= 1.0;
const double sw = 200.0 * double(STREAM_RATE) / 44100.0;
const double d = 2.0 + sw * (0.5 + 0.5 * std::sin(2.0 * M_PI * m_fl_phase));
const double tl = m_fl_l.read(d), tr = m_fl_r.read(d);
m_fl_l.write(dl + 0.3 * tl); m_fl_r.write(dr + 0.3 * tr);
ol = 0.5 * dl + 0.5 * tl; orr = 0.5 * dr + 0.5 * tr;
}
else if (cmb2 == 10) // + VIBRATO (single swept tap, mostly wet)
{
m_vib_phase += c2_minc; if (m_vib_phase >= 1.0) m_vib_phase -= 1.0;
const double sw = 120.0 * double(STREAM_RATE) / 44100.0;
const double d = sw + sw * (0.5 + 0.5 * std::sin(2.0 * M_PI * m_vib_phase));
m_vib_l.write(dl); m_vib_r.write(dr);
ol = 0.4 * dl + 0.6 * m_vib_l.read(d); orr = 0.4 * dr + 0.6 * m_vib_r.read(d);
}
else if (cmb2 == 11) // + PHASER (swept all-pass cascade + feedback)
{
m_ph_phase += c2_minc; if (m_ph_phase >= 1.0) m_ph_phase -= 1.0;
const double a = std::clamp(c2_ac + c2_ad * std::sin(2.0 * M_PI * m_ph_phase), -0.98, 0.98);
for (int i = 0; i < PH_STAGES; i++) { m_ph_l[i].set_a(a); m_ph_r[i].set_a(a); }
double xl = dl + 0.3 * m_ph_fbl, xr = dr + 0.3 * m_ph_fbr;
for (int i = 0; i < PH_STAGES; i++) { xl = m_ph_l[i].process_one(xl); xr = m_ph_r[i].process_one(xr); }
m_ph_fbl = xl; m_ph_fbr = xr;
ol = 0.5 * dl + 0.5 * xl; orr = 0.5 * dr + 0.5 * xr;
}
else if (cmb2 == 12) // + S.DELAY (a second feedback delay in series)
{
const double t2l = m_dly2_l.read(double(c2_dN)), t2r = m_dly2_r.read(double(c2_dN));
m_dly2_l.write(dl + c2_gfb2 * t2l);
m_dly2_r.write(dr + c2_gfb2 * t2r);
ol = 0.5 * dl + 0.5 * t2l; orr = 0.5 * dr + 0.5 * t2r;
}
mix_l = int32_t(ol * 32768.0);
mix_r = int32_t(orr * 32768.0);
}