Exciter — HLE reconstruction and DSP bytecode

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

high-band waveshaper (DRIVE=0x00) + tone + dry mix; adds upper harmonics. Roles INFERRED.

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/prog35_exciter.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 -- EXCITER
; image rep algo 35  |  slots 35  |  unit 0 (I-RAM load 84)
; family exciter  |  confidence high  |  69 words, 22 class-A multiplies (18 named)
; role: harmonic exciter: LUT -> band-pass -> +dry
; 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    08801308BC   ?word   0x08801308BC   ; 880.1.30.8BC  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    000020B1CD   ld      (p),(p)+11
  w2    000020040E   ld      acc,(p)+0
  w3    0212200000   mac.b   (p)0,(p)+0 ; mem[p]<-acc, acc=0
  w4    002A201407   ?word   0x002A201407   ; 02A.2.01.407  hi12{f31=5 ?5 res=020}  [SPECULATIVE: class-2 post-increment MAC (source 0x10); the accumulator-combine f31=5 and/or ACT 0x07 are OPEN]
  w5    000023F407   ld.st   acc,(p)+63
  w6    0026200000   ?word   0x0026200000   ; 026.2.00.000  hi12{f31=3 ?5 res=020}  [SPECULATIVE (prospective, not measured): SRC 0x00 = mem[ptr]/delay-RAM read]
  w7    0000200415   ld      acc,(p)+0
  w8    0212A00000   mac.b   (p)0,c+,(p)+0 ; mem[p]<-acc, acc=0
        ; C-RAM[0x00] (coeff, base 0x00 MEASURED)
        ; coeff C-RAM[0x00] = op0x61[0] (role coeff, INFERRED)
  w9    00922C1700   ?word   0x00922C1700   ; 092.2.C1.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)]
  w10   0000A00415   ld      acc,c+,(p)+0
        ; C-RAM[0x01] (coeff, base 0x00 MEASURED)
  w11   018223F000   mac.b   (p)0,(p)+63
  w12   0040000C63   ?word   0x0040000C63   ; 040.0.00.C63  hi12{?6 res=040}  [SPECULATIVE (prospective, not measured): SRC 0x11 = ACCB (2nd accumulator)]
  w13   00006284CD   ?word   0x00006284CD   ; 000.6.28.4CD  hi12{-}  [table-lookup idiom, class-6 addr8 = table selector (INFERRED)]
  w14   00124011CE   ?word   0x00124011CE   ; 012.4.01.1CE  hi12{ST f31=1}  [table-lookup idiom, third word (INFERRED)]
  w15   01042001CE   post    (p),(p)+0
  w16   0102200000   mac.b   (p)0,(p)+0
  w17   0000A00415   ld      acc,c+,(p)+0
        ; C-RAM[0x02] (coeff, base 0x00 MEASURED)
        ; coeff C-RAM[0x02] = op0x62[0] (role output-level, INFERRED)
  w18   0212A001D3   mac.ta  (p),c+,(p)+0 ; mem[p]<-acc, acc=0
        ; C-RAM[0x03] (coeff, base 0x00 MEASURED)
        ; coeff C-RAM[0x03] = biquad b1 (role EQ, PROVEN)
  w19   0212A01412   mac     acc,c+,(p)+1 ; mem[p]<-acc, acc=0
        ; C-RAM[0x04] (coeff, base 0x00 MEASURED)
        ; coeff C-RAM[0x04] = biquad b0 (role EQ, PROVEN)
  w20   0202A011D5   mac     (p),c+,(p)+1
        ; C-RAM[0x05] (coeff, base 0x00 MEASURED)
        ; coeff C-RAM[0x05] = biquad b2 (role EQ, PROVEN)
  w21   0202A011D4   mac.tb  (p),c+,(p)+1
        ; C-RAM[0x06] (coeff, base 0x00 MEASURED)
        ; coeff C-RAM[0x06] = biquad -a1 (role EQ, PROVEN)
  w22   0202A001D5   mac     (p),c+,(p)+0
        ; C-RAM[0x07] (coeff, base 0x00 MEASURED)
        ; coeff C-RAM[0x07] = biquad -a2 (role EQ, PROVEN)
  w23   01022FF687   mac.st  tb,(p)-1
  w24   0804816415   post    acc,c
  w25   0212AFF407   mac.st  acc,c+,(p)-1 ; mem[p]<-acc, acc=0
        ; C-RAM[0x08] (coeff, base 0x00 MEASURED)
        ; coeff C-RAM[0x08] = biquad makeup (role EQ, PROVEN)
  w26   00002C0647   ld.st   ta,(p)-64
  w27   0022200000   mac.b   (p)0,(p)+0
  w28   0000A00415   ld      acc,c+,(p)+0
        ; C-RAM[0x09] (coeff, base 0x00 MEASURED)
        ; coeff C-RAM[0x09] = op0x66[0] (role mix/tap, INFERRED)
  w29   0212AF41D5   mac     (p),c+,(p)-12 ; mem[p]<-acc, acc=0
        ; C-RAM[0x0A] (coeff, base 0x00 MEASURED)
  w30   02022091CD   mac     (p),(p)+9
  w31   000020340E   ld      acc,(p)+3
  w32   02122FD000   mac.b   (p)0,(p)-3 ; mem[p]<-acc, acc=0
  w33   0028200000   ?word   0x0028200000   ; 028.2.00.000  hi12{f31=4 ?5 res=020}  [SPECULATIVE (prospective, not measured): SRC 0x00 = mem[ptr]/delay-RAM read]
  w34   0880130407   dly.r  dsc[k],p+48
  w35   00002F7000   nop
  w36   000020A1CD   ld      (p),(p)+10
  w37   00002021CE   ld      (p),(p)+2
  w38   0212200000   mac.b   (p)0,(p)+0 ; mem[p]<-acc, acc=0
  w39   002A200000   ?word   0x002A200000   ; 02A.2.00.000  hi12{f31=5 ?5 res=020}  [SPECULATIVE (prospective, not measured): SRC 0x00 = mem[ptr]/delay-RAM read]
  w40   0000243407   ld.st   acc,(p)+67
  w41   0026200000   ?word   0x0026200000   ; 026.2.00.000  hi12{f31=3 ?5 res=020}  [SPECULATIVE (prospective, not measured): SRC 0x00 = mem[ptr]/delay-RAM read]
  w42   0000200415   ld      acc,(p)+0
  w43   0212A00000   mac.b   (p)0,c+,(p)+0 ; mem[p]<-acc, acc=0
        ; C-RAM[0x0B] (coeff, base 0x00 MEASURED)
        ; coeff C-RAM[0x0B] = op0x61[1] (role coeff, INFERRED)
  w44   00922BD700   ?word   0x00922BD700   ; 092.2.BD.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)]
  w45   0000A00415   ld      acc,c+,(p)+0
        ; C-RAM[0x0C] (coeff, base 0x00 MEASURED)
  w46   0182243000   mac.b   (p)0,(p)+67
  w47   0040000C63   ?word   0x0040000C63   ; 040.0.00.C63  hi12{?6 res=040}  [SPECULATIVE (prospective, not measured): SRC 0x11 = ACCB (2nd accumulator)]
  w48   00006284CD   ?word   0x00006284CD   ; 000.6.28.4CD  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   01042001CE   post    (p),(p)+0
  w51   0102200000   mac.b   (p)0,(p)+0
  w52   0000A00415   ld      acc,c+,(p)+0
        ; C-RAM[0x0D] (coeff, base 0x00 MEASURED)
        ; coeff C-RAM[0x0D] = op0x62[1] (role output-level, INFERRED)
  w53   0212A001D3   mac.ta  (p),c+,(p)+0 ; mem[p]<-acc, acc=0
        ; C-RAM[0x0E] (coeff, base 0x00 MEASURED)
        ; coeff C-RAM[0x0E] = biquad b1 (role EQ, PROVEN)
  w54   0212A01412   mac     acc,c+,(p)+1 ; mem[p]<-acc, acc=0
        ; C-RAM[0x0F] (coeff, base 0x00 MEASURED)
        ; coeff C-RAM[0x0F] = biquad b0 (role EQ, PROVEN)
  w55   0202A011D5   mac     (p),c+,(p)+1
        ; C-RAM[0x10] (coeff, base 0x00 MEASURED)
        ; coeff C-RAM[0x10] = biquad b2 (role EQ, PROVEN)
  w56   0202A011D4   mac.tb  (p),c+,(p)+1
        ; C-RAM[0x11] (coeff, base 0x00 MEASURED)
        ; coeff C-RAM[0x11] = biquad -a1 (role EQ, PROVEN)
  w57   0202A001D5   mac     (p),c+,(p)+0
        ; C-RAM[0x12] (coeff, base 0x00 MEASURED)
        ; coeff C-RAM[0x12] = biquad -a2 (role EQ, PROVEN)
  w58   01022FF687   mac.st  tb,(p)-1
  w59   0804816415   post    acc,c
  w60   0212AFF407   mac.st  acc,c+,(p)-1 ; mem[p]<-acc, acc=0
        ; C-RAM[0x13] (coeff, base 0x00 MEASURED)
        ; coeff C-RAM[0x13] = biquad makeup (role EQ, PROVEN)
  w61   00002BC647   ld.st   ta,(p)-68
  w62   0022200000   mac.b   (p)0,(p)+0
  w63   0000A00415   ld      acc,c+,(p)+0
        ; C-RAM[0x14] (coeff, base 0x00 MEASURED)
        ; coeff C-RAM[0x14] = op0x66[1] (role mix/tap, INFERRED)
  w64   0212AF41D5   mac     (p),c+,(p)-12 ; mem[p]<-acc, acc=0
        ; C-RAM[0x15] (coeff, base 0x00 MEASURED)
  w65   02022FB1CD   mac     (p),(p)-5
  w66   000021140E   ld      acc,(p)+17
  w67   02122EF000   mac.b   (p)0,(p)-17 ; mem[p]<-acc, acc=0
  w68   042810E000   ?word   0x042810E000   ; 428.1.0E.000  hi12{END f31=4 ?5 res=020}  [END OF BLOCK, unit 0 -- CALL/RETURN -- and still performs the rest of the word]

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 exc_hle = (dsphle == 0x11);
	double exc_drive = 0.0, exc_mix = 0.0;
	if (exc_hle)
	{
		exc_drive = std::clamp(3.0 + 24.0 * std::fabs(q22x(m_dsp1->cram_read(0x00)) * x_cs), 3.0, 30.0);
		exc_mix   = std::clamp(0.30 + 0.70 * std::fabs(q22x(m_dsp1->cram_read(0x09)) * x_cs), 0.30, 1.0);
		m_exc_lp_l.set_damping(0.64); m_exc_lp_r.set_damping(0.64);   // ~3.5 kHz band split
	}

Per-sample insert (the reconstructed signal path):

		if (exc_hle)   // EXCITER: high band -> tanh (upper harmonics) -> added to the dry signal
		{
			const double xl = double(mix_l) / 32768.0, xr = double(mix_r) / 32768.0;
			const double hl = xl - m_exc_lp_l.process_one(xl), hr = xr - m_exc_lp_r.process_one(xr);
			const double yl = xl + exc_mix * std::tanh(hl * exc_drive);
			const double yr = xr + exc_mix * std::tanh(hr * exc_drive);
			mix_l = int32_t(std::clamp(yl, -1.0, 1.0) * 32767.0);
			mix_r = int32_t(std::clamp(yr, -1.0, 1.0) * 32767.0);
		}