S.Delay + Chorus — HLE + bytecode
S.Delay + Chorus — HLE reconstruction and DSP bytecode
DSPHLE selector 0x08 · program image prog64_s_delay_chorus · Intervention-pinned (a panel control was driven and the moving C-RAM cell identified).
delay (desc 0x26) chained into a chorus (rate=0x00, feedback=0x02).
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/prog64_s_delay_chorus.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+CHORUS
; image rep algo 64 | slots 64 | unit 0 (I-RAM load 84)
; family combi | confidence high | 95 words, 23 class-A multiplies (14 named)
; role: single delay + chorus
; 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 0092A03200 mac.b c,c+,(p)+3 ; store SUPPRESSED (bit7)
; C-RAM[0x00] (coeff, base 0x00 MEASURED)
w1 00822001C0 mac.b (p),(p)+0
w2 0094A00200 wrap acc,c+ ; acc <- datum(acc) & coef (LFO modulus)
; C-RAM[0x01] (coeff, base 0x00 MEASURED)
w3 0880130447 ?word 0x0880130447 ; 880.1.30.447 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]
w4 002220A000 mac.b (p)0,(p)+10
w5 00002FE407 ld.st acc,(p)-2
w6 0000A001D5 ld (p),c+,(p)+0
; C-RAM[0x02] (coeff, base 0x00 MEASURED)
; coeff C-RAM[0x02] = filter section cell 0 (role filter, INFERRED)
w7 08801602DA ?word 0x08801602DA ; 880.1.60.2DA hi12{ESC ?7 res=080} [external delay-DRAM WRITE (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); the line BASE -- MULTI TAP DELAY's four taps share exactly one of these, which is what forces the polarity. 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]
w8 0202A00695 mac tb,c+,(p)+0
; C-RAM[0x03] (coeff, base 0x00 MEASURED)
; coeff C-RAM[0x03] = filter section cell 1 (role filter, INFERRED)
w9 0000200000 nop
w10 0212200419 mac.ta2 acc,(p)+0 ; mem[p]<-acc, acc=0
w11 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]
w12 0000202407 ld.st acc,(p)+2
w13 00002001D5 ld (p),(p)+0
w14 0202AF3415 mac acc,c+,(p)-13
; C-RAM[0x04] (coeff, base 0x00 MEASURED)
; coeff C-RAM[0x04] = op0x66[0] (role mix/tap, INFERRED)
w15 020420D1CD post (p),(p)+13
w16 000020040E ld acc,(p)+0
w17 0212200000 mac.b (p)0,(p)+0 ; mem[p]<-acc, acc=0
w18 0000202407 ld.st acc,(p)+2
w19 09001601D5 dly.w dsc[k],p+96
w20 0192A3C000 mac.b (p)0,c+,(p)+60 ; store SUPPRESSED (bit7)
; C-RAM[0x05] (coeff, base 0x00 MEASURED)
; coeff C-RAM[0x05] = filter section cell 3 (role filter, INFERRED)
w21 00822001C0 mac.b (p),(p)+0
w22 0C4032044C ldreg r4C,#25 ; immediate -> the register lo12 selects
w23 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]
w24 08801202C7 dly.r dsc[k],p+32
w25 0102AC24C8 ?word 0x0102AC24C8 ; 102.A.C2.4C8 hi12{f98=1 f31=1} cur+ [gain multiply (same op in phaser all-pass and reverb diffuser)]
; C-RAM[0x06] (coeff, base 0x00 MEASURED)
; coeff C-RAM[0x06] = filter section cell 4 (role filter, INFERRED)
w26 00002FF407 ld.st acc,(p)-1
w27 00122F51C0 mac.b (p),(p)-11 ; mem[p]<-acc, acc=0
w28 09001601D5 dly.w dsc[k],p+96
w29 0192A4B000 mac.b (p)0,c+,(p)+75 ; store SUPPRESSED (bit7)
; C-RAM[0x07] (coeff, base 0x00 MEASURED)
w30 00822001C0 mac.b (p),(p)+0
w31 0C4032044C ldreg r4C,#25 ; immediate -> the register lo12 selects
w32 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]
w33 08801202C7 dly.r dsc[k],p+32
w34 0102AC04C8 ?word 0x0102AC04C8 ; 102.A.C0.4C8 hi12{f98=1 f31=1} cur+ [gain multiply (same op in phaser all-pass and reverb diffuser)]
; C-RAM[0x08] (coeff, base 0x00 MEASURED)
w35 0000AFF1D5 ld (p),c+,(p)-1
; C-RAM[0x09] (coeff, base 0x00 MEASURED)
w36 02022001D5 mac (p),(p)+0
w37 0202AF6415 mac acc,c+,(p)-10
; C-RAM[0x0A] (coeff, base 0x00 MEASURED)
; coeff C-RAM[0x0A] = op0x66[1] (role mix/tap, INFERRED)
w38 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)]
w39 0204A061D5 post (p),c+,(p)+6
; C-RAM[0x0B] (coeff, base 0x00 MEASURED)
w40 0182200407 mac.st acc,(p)+0
w41 0040000C63 ?word 0x0040000C63 ; 040.0.00.C63 hi12{?6 res=040} [SPECULATIVE (prospective, not measured): SRC 0x11 = ACCB (2nd accumulator)]
w42 00006184CD ?word 0x00006184CD ; 000.6.18.4CD hi12{-} [table-lookup idiom, class-6 addr8 = table selector (INFERRED)]
w43 00124011CE ?word 0x00124011CE ; 012.4.01.1CE hi12{ST f31=1} [table-lookup idiom, third word (INFERRED)]
w44 01042061CE post (p),(p)+6
w45 0142000C63 ?word 0x0142000C63 ; 142.0.00.C63 hi12{f98=1 f31=1 ?6 res=040} [SPECULATIVE (prospective, not measured): SRC 0x11 = ACCB (2nd accumulator)]
w46 000061E407 ?word 0x000061E407 ; 000.6.1E.407 hi12{-} [table-lookup idiom, class-6 addr8 = table selector (INFERRED)]
w47 00124011CE ?word 0x00124011CE ; 012.4.01.1CE hi12{ST f31=1} [table-lookup idiom, third word (INFERRED)]
w48 01042F31CE post (p),(p)-13
w49 0102200000 mac.b (p)0,(p)+0
w50 0000A00415 ld acc,c+,(p)+0
; C-RAM[0x0C] (coeff, base 0x00 MEASURED)
; coeff C-RAM[0x0C] = op0x66[2] (role mix/tap, INFERRED)
w51 0212200000 mac.b (p)0,(p)+0 ; mem[p]<-acc, acc=0
w52 000020D407 ld.st acc,(p)+13
w53 0010A001D5 ld (p),c+,(p)+0 ; mem[p]<-acc, acc=0
; C-RAM[0x0D] (coeff, base 0x00 MEASURED)
; coeff C-RAM[0x0D] = op0x66[3] (role mix/tap, INFERRED)
w54 0202200000 mac.b (p)0,(p)+0
w55 00002FB407 ld.st acc,(p)-5
w56 00122031C0 mac.b (p),(p)+3 ; mem[p]<-acc, acc=0
w57 0022200000 mac.b (p)0,(p)+0
w58 0880130407 dly.r dsc[k],p+48
w59 0000200000 nop
w60 00002FF000 nop
w61 0000A001D5 ld (p),c+,(p)+0
; C-RAM[0x0E] (coeff, base 0x00 MEASURED)
; coeff C-RAM[0x0E] = filter section cell 0 (role filter, INFERRED)
w62 08801602DA ?word 0x08801602DA ; 880.1.60.2DA hi12{ESC ?7 res=080} [external delay-DRAM WRITE (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); the line BASE -- MULTI TAP DELAY's four taps share exactly one of these, which is what forces the polarity. 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]
w63 0202A00695 mac tb,c+,(p)+0
; C-RAM[0x0F] (coeff, base 0x00 MEASURED)
; coeff C-RAM[0x0F] = filter section cell 1 (role filter, INFERRED)
w64 0000200000 nop
w65 0212200419 mac.ta2 acc,(p)+0 ; mem[p]<-acc, acc=0
w66 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]
w67 0000201407 ld.st acc,(p)+1
w68 00002001D5 ld (p),(p)+0
w69 0202AF3415 mac acc,c+,(p)-13
; C-RAM[0x10] (coeff, base 0x00 MEASURED)
; coeff C-RAM[0x10] = op0x66[4] (role mix/tap, INFERRED)
w70 020420D1CD post (p),(p)+13
w71 000020040E ld acc,(p)+0
w72 0212200000 mac.b (p)0,(p)+0 ; mem[p]<-acc, acc=0
w73 0000202407 ld.st acc,(p)+2
w74 09001601D5 dly.w dsc[k],p+96
w75 0192A40000 mac.b (p)0,c+,(p)+64 ; store SUPPRESSED (bit7)
; C-RAM[0x11] (coeff, base 0x00 MEASURED)
; coeff C-RAM[0x11] = filter section cell 3 (role filter, INFERRED)
w76 00822001C0 mac.b (p),(p)+0
w77 0C4032044C ldreg r4C,#25 ; immediate -> the register lo12 selects
w78 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]
w79 08801202C7 dly.r dsc[k],p+32
w80 0102ABE4C8 ?word 0x0102ABE4C8 ; 102.A.BE.4C8 hi12{f98=1 f31=1} cur+ [gain multiply (same op in phaser all-pass and reverb diffuser)]
; C-RAM[0x12] (coeff, base 0x00 MEASURED)
; coeff C-RAM[0x12] = filter section cell 4 (role filter, INFERRED)
w81 00002FF407 ld.st acc,(p)-1
w82 00122F51C0 mac.b (p),(p)-11 ; mem[p]<-acc, acc=0
w83 09001601D5 dly.w dsc[k],p+96
w84 0192A4F000 mac.b (p)0,c+,(p)+79 ; store SUPPRESSED (bit7)
; C-RAM[0x13] (coeff, base 0x00 MEASURED)
w85 00822001C0 mac.b (p),(p)+0
w86 0C4032044C ldreg r4C,#25 ; immediate -> the register lo12 selects
w87 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]
w88 08801202C7 dly.r dsc[k],p+32
w89 0102ABC4C8 ?word 0x0102ABC4C8 ; 102.A.BC.4C8 hi12{f98=1 f31=1} cur+ [gain multiply (same op in phaser all-pass and reverb diffuser)]
; C-RAM[0x14] (coeff, base 0x00 MEASURED)
w90 0000AFF1D5 ld (p),c+,(p)-1
; C-RAM[0x15] (coeff, base 0x00 MEASURED)
w91 02022001D5 mac (p),(p)+0
w92 0202AEE415 mac acc,c+,(p)-18
; C-RAM[0x16] (coeff, base 0x00 MEASURED)
; coeff C-RAM[0x16] = op0x66[5] (role mix/tap, INFERRED)
w93 0880160000 dly.w dsc[k],p+96
w94 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 bool cmb_sdcho = (dsphle == 8);
int cmb_dN = 0;
double cmb_gfb = 0.0, cmb_cinc = 0.0, cmb_csweep = 0.0;
if (cmb_sdcho)
{
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));
cmb_dN = std::clamp(int(n441 * double(STREAM_RATE) / 44100.0 + 0.5), 1, 34999);
cmb_gfb = std::clamp(q22b(m_dsp1->cram_read(0x02)) * cs * 0.5, -0.95, 0.95); // delay feedback
const double inc = double(m_dsp1->cram_read(0x00) & 0xffffff) * cs; // chorus rate
cmb_cinc = std::clamp(inc * 44100.0 / 8388608.0, 0.02, 12.0) / double(STREAM_RATE);
cmb_csweep = 120.0 * double(STREAM_RATE) / 44100.0; // fixed chorus sweep (not pinned in combo)
}
Per-sample insert (the reconstructed signal path):
if (cmb_sdcho)
{
const double sl = double(mix_l) / 32768.0, sr = double(mix_r) / 32768.0;
// stage 1: single delay
const double dtl = m_dly_l.read(double(cmb_dN)), dtr = m_dly_r.read(double(cmb_dN));
m_dly_l.write(sl + cmb_gfb * dtl);
m_dly_r.write(sr + cmb_gfb * dtr);
const double dl = 0.5 * sl + 0.5 * dtl, dr = 0.5 * sr + 0.5 * dtr;
// stage 2: chorus on the delayed signal
m_cho_phase += cmb_cinc;
if (m_cho_phase >= 1.0) m_cho_phase -= 1.0;
const double cs2 = std::sin(2.0 * M_PI * m_cho_phase), cc2 = std::cos(2.0 * M_PI * m_cho_phase);
m_cho_l.write(dl); m_cho_r.write(dr);
const double wl = 0.5 * (m_cho_l.read(cmb_csweep * (1.0 + 0.5 * cs2)) + m_cho_l.read(cmb_csweep * (1.0 + 0.5 * cc2)));
const double wr = 0.5 * (m_cho_r.read(cmb_csweep * (1.0 + 0.5 * cs2)) + m_cho_r.read(cmb_csweep * (1.0 + 0.5 * cc2)));
mix_l = int32_t((0.6 * dl + 0.4 * wl) * 32768.0);
mix_r = int32_t((0.6 * dr + 0.4 * wr) * 32768.0);
}