Auto Wah — HLE reconstruction and DSP bytecode

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

envelope-swept resonant filter (no LFO); biquad cells 0x00-0x06. Roles SPECULATIVE.

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/prog52_auto_wah.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 -- AUTO WAH
; image rep algo 52  |  slots 52  |  unit 0 (I-RAM load 84)
; family filter  |  confidence medium  |  72 words, 13 class-A multiplies (6 named)
; role: auto wah: envelope-swept resonator
; 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    000020B1CD   ld      (p),(p)+11
  w1    000020340E   ld      acc,(p)+3
  w2    02122FD407   mac.st  acc,(p)-3 ; mem[p]<-acc, acc=0
  w3    002A200000   ?word   0x002A200000   ; 02A.2.00.000  hi12{f31=5 ?5 res=020}  [SPECULATIVE (prospective, not measured): SRC 0x00 = mem[ptr]/delay-RAM read]
  w4    0022200000   mac.b   (p)0,(p)+0
  w5    0880130000   dly.r  dsc[k],p+48
  w6    0000200413   ld.ta   acc,(p)+0
  w7    00002401CD   ld      (p),(p)+64
  w8    01122031CE   mac     (p),(p)+3 ; mem[p]<-acc, acc=0
  w9    01042001CE   post    (p),(p)+0
  w10   02022FF1D5   mac     (p),(p)-1
  w11   0102ABF1D4   mac.tb  (p),c+,(p)-65
        ; C-RAM[0x00] (coeff, base 0x00 MEASURED)
  w12   02042421CD   post    (p),(p)+66
  w13   02042FE687   post.st tb,(p)-2
  w14   08048161DA   ?word   0x08048161DA   ; 804.8.16.1DA  hi12{ESC f31=2} cur  [class 8: post-sum step (rescale/round/saturate?), OPERATION UNKNOWN]
  w15   00002FF647   ld.st   ta,(p)-1
  w16   0000202687   ld.st   tb,(p)+2
  w17   00002BC407   ld.st   acc,(p)-68
  w18   0024200000   post.b  (p)0,(p)+0
  w19   0000205407   ld.st   acc,(p)+5
  w20   0010AFC1D5   ld      (p),c+,(p)-4 ; mem[p]<-acc, acc=0
        ; C-RAM[0x01] (coeff, base 0x00 MEASURED)
  w21   0202A031D5   mac     (p),c+,(p)+3
        ; C-RAM[0x02] (coeff, base 0x00 MEASURED)
  w22   0202200000   mac.b   (p)0,(p)+0
  w23   002A200000   ?word   0x002A200000   ; 02A.2.00.000  hi12{f31=5 ?5 res=020}  [SPECULATIVE (prospective, not measured): SRC 0x00 = mem[ptr]/delay-RAM read]
  w24   0026200000   ?word   0x0026200000   ; 026.2.00.000  hi12{f31=3 ?5 res=020}  [SPECULATIVE (prospective, not measured): SRC 0x00 = mem[ptr]/delay-RAM read]
  w25   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[0x03] (coeff, base 0x00 MEASURED)
  w26   0104A001D5   post    (p),c+,(p)+0
        ; C-RAM[0x04] (coeff, base 0x00 MEASURED)
  w27   0C402C0000   ldreg   r00,#22          ; immediate -> the register lo12 selects
  w28   0182A00000   mac.b   (p)0,c+,(p)+0
        ; C-RAM[0x05] (coeff, base 0x00 MEASURED)
  w29   0000200447   ?word   0x0000200447   ; 000.2.00.447  hi12{-}  [SPECULATIVE (prospective, not measured): SRC 0x11 = ACCB (2nd accumulator)]
  w30   0010AFA1D5   ld      (p),c+,(p)-6 ; mem[p]<-acc, acc=0
        ; C-RAM[0x06] (coeff, base 0x00 MEASURED)
  w31   0202AFE1D5   mac     (p),c+,(p)-2
        ; C-RAM[0x07] (coeff, base 0x00 MEASURED)
        ; coeff C-RAM[0x07] = op0x78[0] (role coeff, INFERRED)
  w32   0202200000   mac.b   (p)0,(p)+0
  w33   0000A00415   ld      acc,c+,(p)+0
        ; C-RAM[0x08] (coeff, base 0x00 MEASURED)
        ; coeff C-RAM[0x08] = op0x78[1] (role coeff, INFERRED)
  w34   0212200000   mac.b   (p)0,(p)+0 ; mem[p]<-acc, acc=0
  w35   0000200407   ld.st   acc,(p)+0
  w36   00002FF1CD   ld      (p),(p)-1
  w37   000020040E   ld      acc,(p)+0
  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   0000200415   ld      acc,(p)+0
  w41   0212200000   mac.b   (p)0,(p)+0 ; mem[p]<-acc, acc=0
  w42   0000200415   ld      acc,(p)+0
  w43   0212200000   mac.b   (p)0,(p)+0 ; mem[p]<-acc, acc=0
  w44   0000A00415   ld      acc,c+,(p)+0
        ; C-RAM[0x09] (coeff, base 0x00 MEASURED)
        ; coeff C-RAM[0x09] = op0x24[0] (role coeff, INFERRED)
  w45   0212A081D5   mac     (p),c+,(p)+8 ; mem[p]<-acc, acc=0
        ; C-RAM[0x0A] (coeff, base 0x00 MEASURED)
        ; coeff C-RAM[0x0A] = op0x24[1] (role coeff, INFERRED)
  w46   0202200000   mac.b   (p)0,(p)+0
  w47   00002F9407   ld.st   acc,(p)-7
  w48   00122071D1   ?word   0x00122071D1   ; 012.2.07.1D1  hi12{ST f31=1}  [SPECULATIVE: class-2 post-increment MAC (source 0x07); the accumulator-combine f31=1 and/or ACT 0x11 are OPEN]
  w49   0292200000   mac.b   (p)0,(p)+0 ; store SUPPRESSED (bit7)
  w50   0000AFF455   ?word   0x0000AFF455   ; 000.A.FF.455  hi12{-} cur+  [SPECULATIVE (prospective, not measured): SRC 0x11 = ACCB (2nd accumulator)]
        ; C-RAM[0x0B] (coeff, base 0x00 MEASURED)
        ; coeff C-RAM[0x0B] = op0x24[2] (role coeff, INFERRED)
  w51   0204200000   post.b  (p)0,(p)+0
  w52   0880130407   dly.r  dsc[k],p+48
  w53   00002F5000   nop
  w54   000020A1CD   ld      (p),(p)+10
  w55   00002011CE   ld      (p),(p)+1
  w56   0212200000   mac.b   (p)0,(p)+0 ; mem[p]<-acc, acc=0
  w57   002A200000   ?word   0x002A200000   ; 02A.2.00.000  hi12{f31=5 ?5 res=020}  [SPECULATIVE (prospective, not measured): SRC 0x00 = mem[ptr]/delay-RAM read]
  w58   0022200000   mac.b   (p)0,(p)+0
  w59   0000200413   ld.ta   acc,(p)+0
  w60   00002441CD   ld      (p),(p)+68
  w61   01122031CE   mac     (p),(p)+3 ; mem[p]<-acc, acc=0
  w62   01042001CE   post    (p),(p)+0
  w63   02022FF1D5   mac     (p),(p)-1
  w64   0102ABB1D4   mac.tb  (p),c+,(p)-69
        ; C-RAM[0x0C] (coeff, base 0x00 MEASURED)
        ; coeff C-RAM[0x0C] = op0x24[3] (role coeff, INFERRED)
  w65   02042461CD   post    (p),(p)+70
  w66   02042FE687   post.st tb,(p)-2
  w67   08048161DA   ?word   0x08048161DA   ; 804.8.16.1DA  hi12{ESC f31=2} cur  [class 8: post-sum step (rescale/round/saturate?), OPERATION UNKNOWN]
  w68   00002FF647   ld.st   ta,(p)-1
  w69   0000202687   ld.st   tb,(p)+2
  w70   00002AA407   ld.st   acc,(p)-86
  w71   042410E000   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 wah_hle = (dsphle == 0x12);
	double wah_q = 0.0;
	if (wah_hle) { wah_q = 0.85; }                               // resonance (SVF feedback)

Per-sample insert (the reconstructed signal path):

		if (wah_hle)   // AUTO WAH: input envelope sweeps a resonant state-variable filter cutoff
		{
			const double xl = double(mix_l) / 32768.0, xr = double(mix_r) / 32768.0;
			m_wah_env_l = std::max(std::fabs(xl), m_wah_env_l * 0.9995);
			m_wah_env_r = std::max(std::fabs(xr), m_wah_env_r * 0.9995);
			const double fcl = 300.0 + std::clamp(m_wah_env_l, 0.0, 1.0) * 3500.0;
			const double fcr = 300.0 + std::clamp(m_wah_env_r, 0.0, 1.0) * 3500.0;
			const double fl = 2.0 * std::sin(PI_C * std::min(fcl, 0.45 * STREAM_RATE) / double(STREAM_RATE));
			const double fr = 2.0 * std::sin(PI_C * std::min(fcr, 0.45 * STREAM_RATE) / double(STREAM_RATE));
			const double dmp = 1.0 - wah_q;
			m_wah_lp_l += fl * m_wah_bp_l; const double hpl = xl - m_wah_lp_l - dmp * m_wah_bp_l; m_wah_bp_l += fl * hpl;
			m_wah_lp_r += fr * m_wah_bp_r; const double hpr = xr - m_wah_lp_r - dmp * m_wah_bp_r; m_wah_bp_r += fr * hpr;
			mix_l = int32_t(std::clamp(m_wah_bp_l, -1.0, 1.0) * 32767.0);
			mix_r = int32_t(std::clamp(m_wah_bp_r, -1.0, 1.0) * 32767.0);
		}