Ensemble — HLE reconstruction and DSP bytecode

DSPHLE selector 0x06 · program image prog06_ensemble · Intervention-pinned (a panel control was driven and the moving C-RAM cell identified).

3-voice LFO-swept delay; rate=0x00, per-voice taps 0x02/0x04/0x06.

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/prog06_ensemble.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 -- ENSEMBLE
; image rep algo 6  |  slots 6  |  unit 0 (I-RAM load 84)
; family modulation  |  confidence medium  |  96 words, 15 class-A multiplies (6 named)
; role: ensemble: multi-voice 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    088016000B   ?word   0x088016000B   ; 880.1.60.00B  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]
  w1    00500008BC   ?word   0x00500008BC   ; 050.0.00.8BC  hi12{ST ?6 res=040}  [SPECULATIVE: lo12 bit-11 modifier word + pointer-mode (bit11-family); the base selector/register is OPEN]
  w2    0092A05200   mac.b   c,c+,(p)+5 ; store SUPPRESSED (bit7)
        ; C-RAM[0x00] (coeff, base 0x00 MEASURED)
  w3    00822001C0   mac.b   (p),(p)+0
  w4    0094A00200   wrap    acc,c+          ; acc <- datum(acc) & coef  (LFO modulus)
        ; C-RAM[0x01] (coeff, base 0x00 MEASURED)
  w5    0000206447   ?word   0x0000206447   ; 000.2.06.447  hi12{-}  [SPECULATIVE (prospective, not measured): SRC 0x11 = ACCB (2nd accumulator)]
  w6    08001201D5   dly.r  dsc[k],p+32
  w7    0192A4041A   ?word   0x0192A4041A   ; 192.A.40.41A  hi12{ST f98=1 f31=1 ?7 res=080} cur+  [SPECULATIVE: class-A multiply (P = coef x source 0x10); the source id / accumulator-combine f31=1 / ACT 0x1A may be OPEN]
        ; C-RAM[0x02] (coeff, base 0x00 MEASURED)
        ; coeff C-RAM[0x02] = ENSEMBLE voice depth (role depth, INFERRED)
  w8    00822001C0   mac.b   (p),(p)+0
  w9    000020044C   ?word   0x000020044C   ; 000.2.00.44C  hi12{-}  [SPECULATIVE (prospective, not measured): SRC 0x11 = ACCB (2nd accumulator); ACT 0x0C = delay READ]
  w10   08801202D9   dly.r  dsc[k],p+32
  w11   0012201655   mac     ta,(p)+1 ; mem[p]<-acc, acc=0
  w12   01042001D5   post    (p),(p)+0
  w13   0102A004C8   ?word   0x0102A004C8   ; 102.A.00.4C8  hi12{f98=1 f31=1} cur+  [gain multiply (same op in phaser all-pass and reverb diffuser)]
        ; C-RAM[0x03] (coeff, base 0x00 MEASURED)
  w14   00202002C7   ld.st   dr,(p)+0
  w15   00022C0680   mac.b   tb,(p)-64
  w16   08001201D5   dly.r  dsc[k],p+32
  w17   0192A4141A   ?word   0x0192A4141A   ; 192.A.41.41A  hi12{ST f98=1 f31=1 ?7 res=080} cur+  [SPECULATIVE: class-A multiply (P = coef x source 0x10); the source id / accumulator-combine f31=1 / ACT 0x1A may be OPEN]
        ; C-RAM[0x04] (coeff, base 0x00 MEASURED)
        ; coeff C-RAM[0x04] = ENSEMBLE voice depth (role depth, INFERRED)
  w18   00822001C0   mac.b   (p),(p)+0
  w19   000020044C   ?word   0x000020044C   ; 000.2.00.44C  hi12{-}  [SPECULATIVE (prospective, not measured): SRC 0x11 = ACCB (2nd accumulator); ACT 0x0C = delay READ]
  w20   08801202D9   dly.r  dsc[k],p+32
  w21   0012201655   mac     ta,(p)+1 ; mem[p]<-acc, acc=0
  w22   01042001D5   post    (p),(p)+0
  w23   0102A004C8   ?word   0x0102A004C8   ; 102.A.00.4C8  hi12{f98=1 f31=1} cur+  [gain multiply (same op in phaser all-pass and reverb diffuser)]
        ; C-RAM[0x05] (coeff, base 0x00 MEASURED)
  w24   00202002C7   ld.st   dr,(p)+0
  w25   00022BF680   mac.b   tb,(p)-65
  w26   08001201D5   dly.r  dsc[k],p+32
  w27   0192A4241A   ?word   0x0192A4241A   ; 192.A.42.41A  hi12{ST f98=1 f31=1 ?7 res=080} cur+  [SPECULATIVE: class-A multiply (P = coef x source 0x10); the source id / accumulator-combine f31=1 / ACT 0x1A may be OPEN]
        ; C-RAM[0x06] (coeff, base 0x00 MEASURED)
        ; coeff C-RAM[0x06] = ENSEMBLE voice depth (role depth, INFERRED)
  w28   00822001C0   mac.b   (p),(p)+0
  w29   000020044C   ?word   0x000020044C   ; 000.2.00.44C  hi12{-}  [SPECULATIVE (prospective, not measured): SRC 0x11 = ACCB (2nd accumulator); ACT 0x0C = delay READ]
  w30   08801202D9   dly.r  dsc[k],p+32
  w31   0012201655   mac     ta,(p)+1 ; mem[p]<-acc, acc=0
  w32   01042001D5   post    (p),(p)+0
  w33   0102A004C8   ?word   0x0102A004C8   ; 102.A.00.4C8  hi12{f98=1 f31=1} cur+  [gain multiply (same op in phaser all-pass and reverb diffuser)]
        ; C-RAM[0x07] (coeff, base 0x00 MEASURED)
  w34   00202002C7   ld.st   dr,(p)+0
  w35   00022B0680   mac.b   tb,(p)-80
  w36   000020E1CD   ld      (p),(p)+14
  w37   00002FB6CE   ?word   0x00002FB6CE   ; 000.2.FB.6CE  hi12{-}  [SPECULATIVE (prospective, not measured): ACT 0x0E = delay/state MIXING: acc onto bus (universal; pair w/ 0x0D)]
  w38   0212205407   mac.st  acc,(p)+5 ; mem[p]<-acc, acc=0
  w39   088016040B   ?word   0x088016040B   ; 880.1.60.40B  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]
  w40   00922F7700   ?word   0x00922F7700   ; 092.2.F7.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)]
  w41   0000A091D5   ld      (p),c+,(p)+9
        ; C-RAM[0x08] (coeff, base 0x00 MEASURED)
  w42   0182200407   mac.st  acc,(p)+0
  w43   0040000C63   ?word   0x0040000C63   ; 040.0.00.C63  hi12{?6 res=040}  [SPECULATIVE (prospective, not measured): SRC 0x11 = ACCB (2nd accumulator)]
  w44   00006184CD   ?word   0x00006184CD   ; 000.6.18.4CD  hi12{-}  [table-lookup idiom, class-6 addr8 = table selector (INFERRED)]
  w45   00124011CE   ?word   0x00124011CE   ; 012.4.01.1CE  hi12{ST f31=1}  [table-lookup idiom, third word (INFERRED)]
  w46   01042FD1CE   post    (p),(p)-3
  w47   0142000C63   ?word   0x0142000C63   ; 142.0.00.C63  hi12{f98=1 f31=1 ?6 res=040}  [SPECULATIVE (prospective, not measured): SRC 0x11 = ACCB (2nd accumulator)]
  w48   000061A407   ?word   0x000061A407   ; 000.6.1A.407  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   01042011CE   post    (p),(p)+1
  w51   0142000C63   ?word   0x0142000C63   ; 142.0.00.C63  hi12{f98=1 f31=1 ?6 res=040}  [SPECULATIVE (prospective, not measured): SRC 0x11 = ACCB (2nd accumulator)]
  w52   0000620407   ?word   0x0000620407   ; 000.6.20.407  hi12{-}  [table-lookup idiom, class-6 addr8 = table selector (INFERRED)]
  w53   00124011CE   ?word   0x00124011CE   ; 012.4.01.1CE  hi12{ST f31=1}  [table-lookup idiom, third word (INFERRED)]
  w54   01042011CE   post    (p),(p)+1
  w55   0142000C63   ?word   0x0142000C63   ; 142.0.00.C63  hi12{f98=1 f31=1 ?6 res=040}  [SPECULATIVE (prospective, not measured): SRC 0x11 = ACCB (2nd accumulator)]
  w56   000061E407   ?word   0x000061E407   ; 000.6.1E.407  hi12{-}  [table-lookup idiom, class-6 addr8 = table selector (INFERRED)]
  w57   00124011CE   ?word   0x00124011CE   ; 012.4.01.1CE  hi12{ST f31=1}  [table-lookup idiom, third word (INFERRED)]
  w58   01042F31CE   post    (p),(p)-13
  w59   010220A1CD   mac     (p),(p)+10
  w60   00002F91CE   ld      (p),(p)-7
  w61   021220C407   mac.st  acc,(p)+12 ; mem[p]<-acc, acc=0
  w62   000020040B   ?word   0x000020040B   ; 000.2.00.40B  hi12{-}  [SPECULATIVE (prospective, not measured): ACT 0x0B = delay-line access (READ/WRITE class-borne)]
  w63   00102FC407   ld.st   acc,(p)-4 ; mem[p]<-acc, acc=0
  w64   08001201D5   dly.r  dsc[k],p+32
  w65   0192A4641A   ?word   0x0192A4641A   ; 192.A.46.41A  hi12{ST f98=1 f31=1 ?7 res=080} cur+  [SPECULATIVE: class-A multiply (P = coef x source 0x10); the source id / accumulator-combine f31=1 / ACT 0x1A may be OPEN]
        ; C-RAM[0x09] (coeff, base 0x00 MEASURED)
        ; coeff C-RAM[0x09] = ENSEMBLE voice depth (role depth, INFERRED)
  w66   00822001C0   mac.b   (p),(p)+0
  w67   000020044C   ?word   0x000020044C   ; 000.2.00.44C  hi12{-}  [SPECULATIVE (prospective, not measured): SRC 0x11 = ACCB (2nd accumulator); ACT 0x0C = delay READ]
  w68   08801202D9   dly.r  dsc[k],p+32
  w69   0012201655   mac     ta,(p)+1 ; mem[p]<-acc, acc=0
  w70   01042001D5   post    (p),(p)+0
  w71   0102A004C8   ?word   0x0102A004C8   ; 102.A.00.4C8  hi12{f98=1 f31=1} cur+  [gain multiply (same op in phaser all-pass and reverb diffuser)]
        ; C-RAM[0x0A] (coeff, base 0x00 MEASURED)
  w72   00202002C7   ld.st   dr,(p)+0
  w73   00022BA680   mac.b   tb,(p)-70
  w74   08001201D5   dly.r  dsc[k],p+32
  w75   0192A4741A   ?word   0x0192A4741A   ; 192.A.47.41A  hi12{ST f98=1 f31=1 ?7 res=080} cur+  [SPECULATIVE: class-A multiply (P = coef x source 0x10); the source id / accumulator-combine f31=1 / ACT 0x1A may be OPEN]
        ; C-RAM[0x0B] (coeff, base 0x00 MEASURED)
        ; coeff C-RAM[0x0B] = ENSEMBLE voice depth (role depth, INFERRED)
  w76   00822001C0   mac.b   (p),(p)+0
  w77   000020044C   ?word   0x000020044C   ; 000.2.00.44C  hi12{-}  [SPECULATIVE (prospective, not measured): SRC 0x11 = ACCB (2nd accumulator); ACT 0x0C = delay READ]
  w78   08801202D9   dly.r  dsc[k],p+32
  w79   0012201655   mac     ta,(p)+1 ; mem[p]<-acc, acc=0
  w80   01042001D5   post    (p),(p)+0
  w81   0102A004C8   ?word   0x0102A004C8   ; 102.A.00.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)
  w82   00202002C7   ld.st   dr,(p)+0
  w83   00022B9680   mac.b   tb,(p)-71
  w84   08001201D5   dly.r  dsc[k],p+32
  w85   0192A4841A   ?word   0x0192A4841A   ; 192.A.48.41A  hi12{ST f98=1 f31=1 ?7 res=080} cur+  [SPECULATIVE: class-A multiply (P = coef x source 0x10); the source id / accumulator-combine f31=1 / ACT 0x1A may be OPEN]
        ; C-RAM[0x0D] (coeff, base 0x00 MEASURED)
        ; coeff C-RAM[0x0D] = ENSEMBLE voice depth (role depth, INFERRED)
  w86   00822001C0   mac.b   (p),(p)+0
  w87   000020044C   ?word   0x000020044C   ; 000.2.00.44C  hi12{-}  [SPECULATIVE (prospective, not measured): SRC 0x11 = ACCB (2nd accumulator); ACT 0x0C = delay READ]
  w88   08801202D9   dly.r  dsc[k],p+32
  w89   0012201655   mac     ta,(p)+1 ; mem[p]<-acc, acc=0
  w90   01042001D5   post    (p),(p)+0
  w91   0102A004C8   ?word   0x0102A004C8   ; 102.A.00.4C8  hi12{f98=1 f31=1} cur+  [gain multiply (same op in phaser all-pass and reverb diffuser)]
        ; C-RAM[0x0E] (coeff, base 0x00 MEASURED)
  w92   00202002C7   ld.st   dr,(p)+0
  w93   00022A5680   mac.b   tb,(p)-91
  w94   0880160000   dly.w  dsc[k],p+96
  w95   040010E000   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 ens_hle = (dsphle == 6);
	const bool vib_hle = (dsphle == 7);
	double ens_inc = 0.0, ens_tap[3] = { 0, 0, 0 };
	double vib_inc = 0.0, vib_depth = 0.0, vib_wet = 0.0;
	if (ens_hle || vib_hle)
	{
		auto q22m = [](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 sr48 = double(STREAM_RATE) / 44100.0;
		if (ens_hle)
		{
			const double inc = double(m_dsp1->cram_read(0x00) & 0xffffff) * cs;
			ens_inc = std::clamp(inc * 44100.0 / 8388608.0, 0.02, 12.0) / double(STREAM_RATE);
			// three per-channel voice tap delays (L cells 0x02/0x04/0x06), in 44.1k samples
			ens_tap[0] = std::clamp(double(m_dsp1->cram_read(0x02) & 0xffffff) * cs, 2.0, 1500.0) * sr48;
			ens_tap[1] = std::clamp(double(m_dsp1->cram_read(0x04) & 0xffffff) * cs, 2.0, 1500.0) * sr48;
			ens_tap[2] = std::clamp(double(m_dsp1->cram_read(0x06) & 0xffffff) * cs, 2.0, 1500.0) * sr48;
		}
		if (vib_hle)
		{
			const double inc = double(m_dsp1->cram_read(0x02) & 0xffffff) * cs;
			vib_inc = std::clamp(inc * 44100.0 / 8388608.0, 0.02, 12.0) / double(STREAM_RATE);
			vib_depth = std::clamp(q22m(m_dsp1->cram_read(0x05)) * cs, 0.0, 1.0) * 240.0 * sr48;  // sweep
			vib_wet   = std::clamp(q22m(m_dsp1->cram_read(0x0C)) * cs, 0.0, 1.0);                  // mix
		}
	}

Per-sample insert (the reconstructed signal path):

		if (ens_hle)
		{
			m_ens_phase += ens_inc;
			if (m_ens_phase >= 1.0) m_ens_phase -= 1.0;
			const double sl = double(mix_l) / 32768.0, sr = double(mix_r) / 32768.0;
			m_ens_l.write(sl); m_ens_r.write(sr);
			double wl = 0.0, wr = 0.0;
			for (int v = 0; v < 3; v++)
			{
				const double ph = m_ens_phase + double(v) / 3.0;   // stagger the voices
				const double m = 0.75 + 0.25 * std::sin(2.0 * M_PI * ph);
				wl += m_ens_l.read(std::max(2.0, ens_tap[v] * m));
				wr += m_ens_r.read(std::max(2.0, ens_tap[v] * m));
			}
			wl /= 3.0; wr /= 3.0;
			mix_l = int32_t((0.5 * sl + 0.5 * wl) * 32768.0);
			mix_r = int32_t((0.5 * sr + 0.5 * wr) * 32768.0);
		}