Multi Tap Delay — HLE + bytecode
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);
}