Multi Tap Delay — HLE reconstruction and DSP bytecode

DSPHLE selector 0x15 · program image prog10_multi_tap_delay · Preview (topology decoded and reconstructed; most panel→cell role mappings are position-decoded).

4 panned taps at the MEASURED host delays 136/272/408/544 ms + feedback.

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/prog10_multi_tap_delay.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 -- MULTI TAP DELAY
; image rep algo 10  |  slots 10  |  unit 0 (I-RAM load 84)
; family delay  |  confidence high  |  68 words, 18 class-A multiplies (11 named)
; role: multi-tap delay: panning taps (partial-cursor-rewind idiom, effect-map 5.1)
; 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    088013000B   ?word   0x088013000B   ; 880.1.30.00B  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    000020F000   nop
  w2    00002FB407   ld.st   acc,(p)-5
  w3    0000A00415   ld      acc,c+,(p)+0
        ; C-RAM[0x00] (coeff, base 0x00 MEASURED)
  w4    0212AF61D5   mac     (p),c+,(p)-10 ; mem[p]<-acc, acc=0
        ; C-RAM[0x01] (coeff, base 0x00 MEASURED)
  w5    020220B1CD   mac     (p),(p)+11
  w6    000020040E   ld      acc,(p)+0
  w7    0212A001D5   mac     (p),c+,(p)+0 ; mem[p]<-acc, acc=0
        ; C-RAM[0x02] (coeff, base 0x00 MEASURED)
  w8    0880160000   dly.w  dsc[k],p+96
  w9    0202200000   mac.b   (p)0,(p)+0
  w10   0000200000   nop
  w11   0000200000   nop
  w12   08801202C7   dly.r  dsc[k],p+32
  w13   0000201000   nop
  w14   0000200000   nop
  w15   0000200000   nop
  w16   08801202C7   dly.r  dsc[k],p+32
  w17   0000201000   nop
  w18   0000200000   nop
  w19   0000200000   nop
  w20   08801202C7   dly.r  dsc[k],p+32
  w21   00002F5000   nop
  w22   0000200000   nop
  w23   0000200000   nop
  w24   08801202C7   dly.r  dsc[k],p+32
  w25   000020940B   ?word   0x000020940B   ; 000.2.09.40B  hi12{-}  [SPECULATIVE (prospective, not measured): ACT 0x0B = delay-line access (READ/WRITE class-borne)]
  w26   0040000864   ?word   0x0040000864   ; 040.0.00.864  hi12{?6 res=040}  [SPECULATIVE: lo12 bit-11 modifier word + pointer-mode (bit11-family); the base selector/register is OPEN]
  w27   0010A011D5   ld      (p),c+,(p)+1 ; mem[p]<-acc, acc=0
        ; C-RAM[0x03] (coeff, base 0x00 MEASURED)
        ; coeff C-RAM[0x03] = op0x66[0] (role mix/tap, INFERRED)
  w28   0202A011D5   mac     (p),c+,(p)+1
        ; C-RAM[0x04] (coeff, base 0x00 MEASURED)
        ; coeff C-RAM[0x04] = op0x66[1] (role mix/tap, INFERRED)
  w29   0202AF51D5   mac     (p),c+,(p)-11
        ; C-RAM[0x05] (coeff, base 0x00 MEASURED)
        ; coeff C-RAM[0x05] = op0x66[2] (role mix/tap, INFERRED)
  w30   0202A0C1D5   mac     (p),c+,(p)+12
        ; C-RAM[0x06] (coeff, base 0x00 MEASURED)
        ; coeff C-RAM[0x06] = op0x66[3] (role mix/tap, INFERRED)
  w31   0202200000   mac.b   (p)0,(p)+0
  w32   00002FD407   ld.st   acc,(p)-3
  w33   0050000921   ?word   0x0050000921   ; 050.0.00.921  hi12{ST ?6 res=040}  [SPECULATIVE: lo12 bit-11 modifier word + pointer-mode (bit11-family); the base selector/register is OPEN]
  w34   0002A031D5   mac     (p),c+,(p)+3
        ; C-RAM[0x07] (coeff, base 0x00 MEASURED)
        ; coeff C-RAM[0x07] = filter section cell 0 (role filter, INFERRED)
  w35   02042FE000   post.b  (p)0,(p)-2
  w36   0002AFF1D5   mac     (p),c+,(p)-1
        ; C-RAM[0x08] (coeff, base 0x00 MEASURED)
        ; coeff C-RAM[0x08] = damping filter tap 0 (role damping, PROVEN)
  w37   0204202000   post.b  (p)0,(p)+2
  w38   0002AFF1D5   mac     (p),c+,(p)-1
        ; C-RAM[0x09] (coeff, base 0x00 MEASURED)
        ; coeff C-RAM[0x09] = damping filter tap 1 (role damping, PROVEN)
  w39   02042F6000   post.b  (p)0,(p)-10
  w40   0002A0B1D5   mac     (p),c+,(p)+11
        ; C-RAM[0x0A] (coeff, base 0x00 MEASURED)
        ; coeff C-RAM[0x0A] = damping filter tap 2 (role damping, PROVEN)
  w41   02042FA000   post.b  (p)0,(p)-6
  w42   0000204407   ld.st   acc,(p)+4
  w43   00102011D5   ld      (p),(p)+1 ; mem[p]<-acc, acc=0
  w44   02022011D5   mac     (p),(p)+1
  w45   02022F51D5   mac     (p),(p)-11
  w46   0202A491D5   mac     (p),c+,(p)+73
        ; C-RAM[0x0B] (coeff, base 0x00 MEASURED)
        ; coeff C-RAM[0x0B] = damping filter tap 0 (role damping, PROVEN)
  w47   0202A001D5   mac     (p),c+,(p)+0
        ; C-RAM[0x0C] (coeff, base 0x00 MEASURED)
        ; coeff C-RAM[0x0C] = damping filter tap 1 (role damping, PROVEN)
  w48   0212A00415   mac     acc,c+,(p)+0 ; mem[p]<-acc, acc=0
        ; C-RAM[0x0D] (coeff, base 0x00 MEASURED)
        ; coeff C-RAM[0x0D] = damping filter tap 2 (role damping, PROVEN)
  w49   0202A001D5   mac     (p),c+,(p)+0
        ; C-RAM[0x0E] (coeff, base 0x00 MEASURED)
  w50   0202200407   mac.st  acc,(p)+0
  w51   0000201000   nop
  w52   0000A001D5   ld      (p),c+,(p)+0
        ; C-RAM[0x0F] (coeff, base 0x00 MEASURED)
  w53   0212A00415   mac     acc,c+,(p)+0 ; mem[p]<-acc, acc=0
        ; C-RAM[0x10] (coeff, base 0x00 MEASURED)
  w54   0202A001D5   mac     (p),c+,(p)+0
        ; C-RAM[0x11] (coeff, base 0x00 MEASURED)
  w55   0202200407   mac.st  acc,(p)+0
  w56   00002BF000   nop
  w57   0000203407   ld.st   acc,(p)+3
  w58   00122F21C0   mac.b   (p),(p)-14 ; mem[p]<-acc, acc=0
  w59   00002091CD   ld      (p),(p)+9
  w60   000020040E   ld      acc,(p)+0
  w61   0212200000   mac.b   (p)0,(p)+0 ; mem[p]<-acc, acc=0
  w62   00002FE407   ld.st   acc,(p)-2
  w63   00122F91C0   mac.b   (p),(p)-7 ; mem[p]<-acc, acc=0
  w64   00002FB1CD   ld      (p),(p)-5
  w65   000020040E   ld      acc,(p)+0
  w66   0880160000   dly.w  dsc[k],p+96
  w67   061210E000   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 mtap_hle = (dsphle == 0x15);
	double mtap_fb = 0.0, mtap_g[4] = { 0, 0, 0, 0 }; int mtap_t[4] = { 0, 0, 0, 0 };
	if (mtap_hle)
	{
		const double base[4] = { 6000.0, 12000.0, 18000.0, 24000.0 };
		for (int i = 0; i < 4; i++) mtap_t[i] = std::clamp(int(base[i] * sr48 + 0.5), 1, 27999);
		mtap_fb = std::clamp(std::fabs(q22x(m_dsp1->cram_read(0x00)) * x_cs), 0.0, 0.7);
		for (int i = 0; i < 4; i++)
			mtap_g[i] = std::clamp(0.4 + 0.6 * std::fabs(q22x(m_dsp1->cram_read(u8(0x03 + i))) * x_cs), 0.2, 1.0);
		m_mtap_dmp_l.set_damping(0.35); m_mtap_dmp_r.set_damping(0.35);
	}

Per-sample insert (the reconstructed signal path):

		if (mtap_hle)  // MULTI TAP DELAY: 4 taps at the measured 136/272/408/544 ms, alternately panned
		{
			const double xl = double(mix_l) / 32768.0, xr = double(mix_r) / 32768.0;
			double sl = 0.0, sr = 0.0;
			for (int i = 0; i < 4; i++)
			{
				const double t = m_mtap_l.read(double(mtap_t[i])), u = m_mtap_r.read(double(mtap_t[i]));
				const double panl = (i & 1) ? 0.4 : 1.0, panr = (i & 1) ? 1.0 : 0.4;   // alternate L/R
				sl += mtap_g[i] * panl * t; sr += mtap_g[i] * panr * u;
			}
			m_mtap_l.write(xl + mtap_fb * m_mtap_dmp_l.process_one(m_mtap_l.read(double(mtap_t[3]))));
			m_mtap_r.write(xr + mtap_fb * m_mtap_dmp_r.process_one(m_mtap_r.read(double(mtap_t[3]))));
			mix_l = int32_t(std::clamp(xl + 0.5 * sl, -1.0, 1.0) * 32767.0);
			mix_r = int32_t(std::clamp(xr + 0.5 * sr, -1.0, 1.0) * 32767.0);
		}