Gated Reverb — HLE + bytecode
Gated Reverb — HLE reconstruction and DSP bytecode
DSPHLE selector 0x17 · program image prog08_gated_reverb · Preview (topology decoded and reconstructed; most panel→cell role mappings are position-decoded).
reverb tank (MEASURED line lengths) + envelope gate chopping the tail; GATE TIME=0x15.
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/prog08_gated_reverb.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 -- GATED REVERB
; image rep algo 8 | slots 8 | unit 0 (I-RAM load 84)
; family reverb | confidence medium | 102 words, 22 class-A multiplies (7 named)
; role: gated reverb: all-pass ring + hold gate
; 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 0000A0A415 ld acc,c+,(p)+10
; C-RAM[0x00] (coeff, base 0x00 MEASURED)
w2 0212AF61D5 mac (p),c+,(p)-10 ; mem[p]<-acc, acc=0
; C-RAM[0x01] (coeff, base 0x00 MEASURED)
w3 020220C1CD mac (p),(p)+12
w4 000020040E ld acc,(p)+0
w5 08801602D9 dly.w dsc[k],p+96
w6 0212A00655 mac ta,c+,(p)+0 ; mem[p]<-acc, acc=0
; C-RAM[0x02] (coeff, base 0x00 MEASURED)
w7 00002F9407 ld.st acc,(p)-7
w8 0212200419 mac.ta2 acc,(p)+0 ; mem[p]<-acc, acc=0
w9 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]
w10 0000200000 nop
w11 0000A461D5 ld (p),c+,(p)+70
; C-RAM[0x03] (coeff, base 0x00 MEASURED)
w12 0202200000 mac.b (p)0,(p)+0
w13 08801602D4 dly.w dsc[k],p+96
w14 0104200000 post.b (p)0,(p)+0
w15 0000200419 ld.ta2 acc,(p)+0
w16 0012200680 mac.b tb,(p)+0 ; mem[p]<-acc, acc=0
w17 0880120655 dly.r dsc[k],p+32
w18 0102A0064B mac ta,c+,(p)+0
; C-RAM[0x04] (coeff, base 0x00 MEASURED)
w19 0000200000 nop
w20 0000200000 nop
w21 08801602D4 dly.w dsc[k],p+96
w22 0104200000 post.b (p)0,(p)+0
w23 0000200419 ld.ta2 acc,(p)+0
w24 0012200680 mac.b tb,(p)+0 ; mem[p]<-acc, acc=0
w25 0880120655 dly.r dsc[k],p+32
w26 0102A0064B mac ta,c+,(p)+0
; C-RAM[0x05] (coeff, base 0x00 MEASURED)
w27 0000200000 nop
w28 0000200000 nop
w29 08801602D4 dly.w dsc[k],p+96
w30 0104200000 post.b (p)0,(p)+0
w31 0000200419 ld.ta2 acc,(p)+0
w32 0012200680 mac.b tb,(p)+0 ; mem[p]<-acc, acc=0
w33 0880120655 dly.r dsc[k],p+32
w34 0102A0064B mac ta,c+,(p)+0
; C-RAM[0x06] (coeff, base 0x00 MEASURED)
w35 0000A001D5 ld (p),c+,(p)+0
; C-RAM[0x07] (coeff, base 0x00 MEASURED)
; coeff C-RAM[0x07] = damping filter tap 0 (role damping, PROVEN)
w36 0212A00415 mac acc,c+,(p)+0 ; mem[p]<-acc, acc=0
; C-RAM[0x08] (coeff, base 0x00 MEASURED)
; coeff C-RAM[0x08] = damping filter tap 1 (role damping, PROVEN)
w37 0202A001D5 mac (p),c+,(p)+0
; C-RAM[0x09] (coeff, base 0x00 MEASURED)
; coeff C-RAM[0x09] = damping filter tap 2 (role damping, PROVEN)
w38 0202200407 mac.st acc,(p)+0
w39 08801602D4 dly.w dsc[k],p+96
w40 0104200000 post.b (p)0,(p)+0
w41 0000200419 ld.ta2 acc,(p)+0
w42 0012200680 mac.b tb,(p)+0 ; mem[p]<-acc, acc=0
w43 0880120655 dly.r dsc[k],p+32
w44 0102AC464B mac ta,c+,(p)-60
; C-RAM[0x0A] (coeff, base 0x00 MEASURED)
w45 0000200000 nop
w46 0000200000 nop
w47 08801602D4 dly.w dsc[k],p+96
w48 0104200000 post.b (p)0,(p)+0
w49 0000200419 ld.ta2 acc,(p)+0
w50 0012200680 mac.b tb,(p)+0 ; mem[p]<-acc, acc=0
w51 0880120655 dly.r dsc[k],p+32
w52 0102A0064B mac ta,c+,(p)+0
; C-RAM[0x0B] (coeff, base 0x00 MEASURED)
w53 0000200000 nop
w54 0000200000 nop
w55 08801602D4 dly.w dsc[k],p+96
w56 0104200000 post.b (p)0,(p)+0
w57 0000200419 ld.ta2 acc,(p)+0
w58 0012200680 mac.b tb,(p)+0 ; mem[p]<-acc, acc=0
w59 0880120655 dly.r dsc[k],p+32
w60 0102A0064B mac ta,c+,(p)+0
; C-RAM[0x0C] (coeff, base 0x00 MEASURED)
w61 0000200000 nop
w62 0000200000 nop
w63 08801602D4 dly.w dsc[k],p+96
w64 0104200407 post.st acc,(p)+0
w65 0000200419 ld.ta2 acc,(p)+0
w66 0012200680 mac.b tb,(p)+0 ; mem[p]<-acc, acc=0
w67 0880120655 dly.r dsc[k],p+32
w68 0102AF764B mac ta,c+,(p)-9
; C-RAM[0x0D] (coeff, base 0x00 MEASURED)
w69 0000246407 ld.st acc,(p)+70
w70 0000A001D5 ld (p),c+,(p)+0
; C-RAM[0x0E] (coeff, base 0x00 MEASURED)
; coeff C-RAM[0x0E] = damping filter tap 0 (role damping, PROVEN)
w71 0212A00415 mac acc,c+,(p)+0 ; mem[p]<-acc, acc=0
; C-RAM[0x0F] (coeff, base 0x00 MEASURED)
; coeff C-RAM[0x0F] = damping filter tap 1 (role damping, PROVEN)
w72 0202A001D5 mac (p),c+,(p)+0
; C-RAM[0x10] (coeff, base 0x00 MEASURED)
; coeff C-RAM[0x10] = damping filter tap 2 (role damping, PROVEN)
w73 02022C0407 mac.st acc,(p)-64
w74 088016041A ?word 0x088016041A ; 880.1.60.41A 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]
w75 00122001C0 mac.b (p),(p)+0 ; mem[p]<-acc, acc=0
w76 002E200000 ?word 0x002E200000 ; 02E.2.00.000 hi12{f31=7 ?5 res=020} [SPECULATIVE (prospective, not measured): SRC 0x00 = mem[ptr]/delay-RAM read]
w77 00202F9000 ld.b (p)0,(p)-7
w78 08801202C7 dly.r dsc[k],p+32
w79 000020868B ?word 0x000020868B ; 000.2.08.68B hi12{-} [SPECULATIVE (prospective, not measured): ACT 0x0B = delay-line access (READ/WRITE class-borne)]
w80 0018A001D5 ?word 0x0018A001D5 ; 018.A.00.1D5 hi12{ST f31=4} cur+ [SPECULATIVE: class-A multiply (P = coef x source 0x07); the source id / accumulator-combine f31=4 / ACT 0x15 may be OPEN]
; C-RAM[0x11] (coeff, base 0x00 MEASURED)
w81 0104A001D5 post (p),c+,(p)+0
; C-RAM[0x12] (coeff, base 0x00 MEASURED)
w82 0C402C0000 ldreg r00,#22 ; immediate -> the register lo12 selects
w83 0182A00000 mac.b (p)0,c+,(p)+0
; C-RAM[0x13] (coeff, base 0x00 MEASURED)
w84 00002F7447 ?word 0x00002F7447 ; 000.2.F7.447 hi12{-} [SPECULATIVE (prospective, not measured): SRC 0x11 = ACCB (2nd accumulator)]
w85 0012A001D5 mac (p),c+,(p)+0 ; mem[p]<-acc, acc=0
; C-RAM[0x14] (coeff, base 0x00 MEASURED)
w86 0C403A0359 ldreg r59,#29 ; immediate -> the register lo12 selects
w87 00922FF1D5 mac (p),(p)-1 ; store SUPPRESSED (bit7)
w88 0184A011D5 post (p),c+,(p)+1
; C-RAM[0x15] (coeff, base 0x00 MEASURED)
; coeff C-RAM[0x15] = op0x6F[0] (role coeff, INFERRED)
w89 01822FF407 mac.st acc,(p)-1
w90 00002041CD ld (p),(p)+4
w91 00002FA1CE ld (p),(p)-6
w92 02122021CD mac (p),(p)+2 ; mem[p]<-acc, acc=0
w93 000020040E ld acc,(p)+0
w94 0212207447 ?word 0x0212207447 ; 212.2.07.447 hi12{ST f98=2 f31=1} [writes mem[ptr] (bit 4); mode 2, so the target IS the pointer]
w95 00002F9407 ld.st acc,(p)-7
w96 000020D1CD ld (p),(p)+13
w97 00002F11CE ld (p),(p)-15
w98 02122FB1CD mac (p),(p)-5 ; mem[p]<-acc, acc=0
w99 000020040E ld acc,(p)+0
w100 0880160000 dly.w dsc[k],p+96
w101 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 grev_hle = (dsphle == 0x17);
double grev_fb = 0.0, grev_apg = 0.0; int grev_cd[4] = { 0, 0, 0, 0 }, grev_ad[2] = { 0, 0 };
double grev_gatethr = 0.0, grev_rel = 0.0;
if (grev_hle)
{
const double cb[4] = { 680.0, 800.0, 1440.0, 1800.0 }, ab[2] = { 458.0, 100.0 }; // measured @44.1k
for (int i = 0; i < 4; i++) grev_cd[i] = std::clamp(int(cb[i] * sr48 + 0.5), 1, 2099);
for (int i = 0; i < 2; i++) grev_ad[i] = std::clamp(int(ab[i] * sr48 + 0.5), 1, 559);
grev_fb = 0.78; // fixed short-tail feedback (gate cuts it)
grev_apg = 0.5;
const double damp = std::clamp(std::fabs(q22x(m_dsp1->cram_read(0x07)) * x_cs) * 0.5, 0.0, 0.7);
for (int i = 0; i < 4; i++) { m_grev_d_l[i].set_damping(damp); m_grev_d_r[i].set_damping(damp); }
grev_gatethr = 0.02; // input level that holds the gate open
// GATE TIME cell 0x15 -> gate release time (how fast the tail is chopped)
const double gt = std::clamp(std::fabs(q22x(m_dsp1->cram_read(0x15)) * x_cs), 0.0, 1.0);
grev_rel = std::exp(-1.0 / (double(STREAM_RATE) * (0.02 + 0.15 * gt))); // 20..170 ms
}
Per-sample insert (the reconstructed signal path):
if (grev_hle) // GATED REVERB: reverb tank (measured lengths) + envelope gate chopping the tail
{
const double xl = double(mix_l) / 32768.0, xr = double(mix_r) / 32768.0;
const double ein = std::max(std::fabs(xl), std::fabs(xr));
m_grev_env = std::max(ein, m_grev_env * 0.999);
const double target = (m_grev_env > grev_gatethr) ? 1.0 : 0.0;
if (target >= m_grev_gain) m_grev_gain = target; // open instantly
else m_grev_gain = target + grev_rel * (m_grev_gain - target); // GATE TIME release
double wl = 0.0, wr = 0.0;
for (int c = 0; c < 4; c++)
{
const double tl = m_grev_c_l[c].read(double(grev_cd[c])), tr = m_grev_c_r[c].read(double(grev_cd[c]));
m_grev_c_l[c].write(xl + grev_fb * m_grev_d_l[c].process_one(tl));
m_grev_c_r[c].write(xr + grev_fb * m_grev_d_r[c].process_one(tr));
wl += tl; wr += tr;
}
for (int a = 0; a < 2; a++)
{
const double dl = m_grev_a_l[a].read(double(grev_ad[a])), dr = m_grev_a_r[a].read(double(grev_ad[a]));
const double yl = -grev_apg * wl + dl, yr = -grev_apg * wr + dr;
m_grev_a_l[a].write(wl + grev_apg * yl); m_grev_a_r[a].write(wr + grev_apg * yr);
wl = yl; wr = yr;
}
mix_l = int32_t(std::clamp(xl + 0.6 * m_grev_gain * wl, -1.0, 1.0) * 32767.0);
mix_r = int32_t(std::clamp(xr + 0.6 * m_grev_gain * wr, -1.0, 1.0) * 32767.0);
}