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);
		}