Phaser — HLE reconstruction and DSP bytecode

DSPHLE selector 0x05 · program image prog05_phaser · Intervention-pinned (a panel control was driven and the moving C-RAM cell identified).

swept all-pass cascade + feedback; rate=0x00, manual=0x06, depth=0x05, feedback=0x02.

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/prog05_phaser.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 -- PHASER
; image rep algo 5  |  slots 5  |  unit 0 (I-RAM load 84)
; family modulation  |  confidence medium  |  106 words, 14 class-A multiplies (9 named)
; role: phaser: LFO-swept first-order all-pass chain (~0.438 +/-0.025), 0.3 feedback
; 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    0092A0E200   mac.b   c,c+,(p)+14 ; store SUPPRESSED (bit7)
        ; C-RAM[0x00] (coeff, base 0x00 MEASURED)
  w2    00822001C0   mac.b   (p),(p)+0
  w3    0094AF2200   wrap    acc,c+          ; acc <- datum(acc) & coef  (LFO modulus)
        ; C-RAM[0x01] (coeff, base 0x00 MEASURED)
  w4    000020C1CD   ld      (p),(p)+12
  w5    000020240E   ld      acc,(p)+2
  w6    02122FE447   ?word   0x02122FE447   ; 212.2.FE.447  hi12{ST f98=2 f31=1}  [writes mem[ptr] (bit 4); mode 2, so the target IS the pointer]
  w7    002A200000   ?word   0x002A200000   ; 02A.2.00.000  hi12{f31=5 ?5 res=020}  [SPECULATIVE (prospective, not measured): SRC 0x00 = mem[ptr]/delay-RAM read]
  w8    00002F8407   ld.st   acc,(p)-8
  w9    0028AFF1D5   ?word   0x0028AFF1D5   ; 028.A.FF.1D5  hi12{f31=4 ?5 res=020} cur+  [SPECULATIVE: class-A multiply (P = coef x source 0x07); the source id / accumulator-combine f31=4 / ACT 0x15 may be OPEN]
        ; C-RAM[0x02] (coeff, base 0x00 MEASURED)
        ; coeff C-RAM[0x02] = filter section cell 0 (role filter, INFERRED)
  w10   02022481C8   ?word   0x02022481C8   ; 202.2.48.1C8  hi12{f98=2 f31=1}  [SPECULATIVE (prospective, not measured): ACT 0x08 = table-port multiply]
  w11   00002BB1D5   ld      (p),(p)-69
  w12   01022451CD   mac     (p),(p)+69
  w13   0212201412   mac     acc,(p)+1 ; mem[p]<-acc, acc=0
  w14   01042BA1D5   post    (p),(p)-70
  w15   01022461CD   mac     (p),(p)+70
  w16   0212201412   mac     acc,(p)+1 ; mem[p]<-acc, acc=0
  w17   01042B91D5   post    (p),(p)-71
  w18   01022471CD   mac     (p),(p)+71
  w19   0212201412   mac     acc,(p)+1 ; mem[p]<-acc, acc=0
  w20   01042B81D5   post    (p),(p)-72
  w21   01022481CD   mac     (p),(p)+72
  w22   0212201412   mac     acc,(p)+1 ; mem[p]<-acc, acc=0
  w23   01042B71D5   post    (p),(p)-73
  w24   01022491CD   mac     (p),(p)+73
  w25   0212201412   mac     acc,(p)+1 ; mem[p]<-acc, acc=0
  w26   01042B61D5   post    (p),(p)-74
  w27   010224A1CD   mac     (p),(p)+74
  w28   0212201412   mac     acc,(p)+1 ; mem[p]<-acc, acc=0
  w29   01042B51D5   post    (p),(p)-75
  w30   010224B1CD   mac     (p),(p)+75
  w31   0212201412   mac     acc,(p)+1 ; mem[p]<-acc, acc=0
  w32   01042B41D5   post    (p),(p)-76
  w33   010224C1CD   mac     (p),(p)+76
  w34   0212201412   mac     acc,(p)+1 ; mem[p]<-acc, acc=0
  w35   01042B31D5   post    (p),(p)-77
  w36   010224D1CD   mac     (p),(p)+77
  w37   0212201412   mac     acc,(p)+1 ; mem[p]<-acc, acc=0
  w38   01042B21D5   post    (p),(p)-78
  w39   010224E1CD   mac     (p),(p)+78
  w40   0212AB0412   mac     acc,c+,(p)-80 ; mem[p]<-acc, acc=0
        ; C-RAM[0x03] (coeff, base 0x00 MEASURED)
        ; coeff C-RAM[0x03] = filter section cell 1 (role filter, INFERRED)
  w41   0104200000   post.b  (p)0,(p)+0
  w42   0880130407   dly.r  dsc[k],p+48
  w43   009220A700   ?word   0x009220A700   ; 092.2.0A.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)]
  w44   0104AFB1D5   post    (p),c+,(p)-5
        ; C-RAM[0x04] (coeff, base 0x00 MEASURED)
        ; coeff C-RAM[0x04] = filter section cell 2 (role filter, INFERRED)
  w45   0182200407   mac.st  acc,(p)+0
  w46   0040000C63   ?word   0x0040000C63   ; 040.0.00.C63  hi12{?6 res=040}  [SPECULATIVE (prospective, not measured): SRC 0x11 = ACCB (2nd accumulator)]
  w47   00006184CD   ?word   0x00006184CD   ; 000.6.18.4CD  hi12{-}  [table-lookup idiom, class-6 addr8 = table selector (INFERRED)]
  w48   00124011CE   ?word   0x00124011CE   ; 012.4.01.1CE  hi12{ST f31=1}  [table-lookup idiom, third word (INFERRED)]
  w49   01042021CE   post    (p),(p)+2
  w50   0102200000   mac.b   (p)0,(p)+0
  w51   0000A00415   ld      acc,c+,(p)+0
        ; C-RAM[0x05] (coeff, base 0x00 MEASURED)
        ; coeff C-RAM[0x05] = op0x66[0] (role mix/tap, INFERRED)
  w52   0212A041D5   mac     (p),c+,(p)+4 ; mem[p]<-acc, acc=0
        ; C-RAM[0x06] (coeff, base 0x00 MEASURED)
        ; coeff C-RAM[0x06] = op0x66[1] (role mix/tap, INFERRED)
  w53   0092200700   ?word   0x0092200700   ; 092.2.00.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)]
  w54   0202AF41D5   mac     (p),c+,(p)-12
        ; C-RAM[0x07] (coeff, base 0x00 MEASURED)
  w55   0182200407   mac.st  acc,(p)+0
  w56   0040000C63   ?word   0x0040000C63   ; 040.0.00.C63  hi12{?6 res=040}  [SPECULATIVE (prospective, not measured): SRC 0x11 = ACCB (2nd accumulator)]
  w57   00006184CD   ?word   0x00006184CD   ; 000.6.18.4CD  hi12{-}  [table-lookup idiom, class-6 addr8 = table selector (INFERRED)]
  w58   00124011CE   ?word   0x00124011CE   ; 012.4.01.1CE  hi12{ST f31=1}  [table-lookup idiom, third word (INFERRED)]
  w59   01042081CE   post    (p),(p)+8
  w60   0102200000   mac.b   (p)0,(p)+0
  w61   0000A00415   ld      acc,c+,(p)+0
        ; C-RAM[0x08] (coeff, base 0x00 MEASURED)
        ; coeff C-RAM[0x08] = op0x66[2] (role mix/tap, INFERRED)
  w62   0212AF51D5   mac     (p),c+,(p)-11 ; mem[p]<-acc, acc=0
        ; C-RAM[0x09] (coeff, base 0x00 MEASURED)
        ; coeff C-RAM[0x09] = op0x66[3] (role mix/tap, INFERRED)
  w63   020220A1CD   mac     (p),(p)+10
  w64   00002F81CE   ld      (p),(p)-8
  w65   021220A407   mac.st  acc,(p)+10 ; mem[p]<-acc, acc=0
  w66   002A200000   ?word   0x002A200000   ; 02A.2.00.000  hi12{f31=5 ?5 res=020}  [SPECULATIVE (prospective, not measured): SRC 0x00 = mem[ptr]/delay-RAM read]
  w67   00002F9407   ld.st   acc,(p)-7
  w68   0028AFD1D5   ?word   0x0028AFD1D5   ; 028.A.FD.1D5  hi12{f31=4 ?5 res=020} cur+  [SPECULATIVE: class-A multiply (P = coef x source 0x07); the source id / accumulator-combine f31=4 / ACT 0x15 may be OPEN]
        ; C-RAM[0x0A] (coeff, base 0x00 MEASURED)
        ; coeff C-RAM[0x0A] = filter section cell 0 (role filter, INFERRED)
  w69   02022531C8   ?word   0x02022531C8   ; 202.2.53.1C8  hi12{f98=2 f31=1}  [SPECULATIVE (prospective, not measured): ACT 0x08 = table-port multiply]
  w70   00002B11D5   ld      (p),(p)-79
  w71   010224F1CD   mac     (p),(p)+79
  w72   0212201412   mac     acc,(p)+1 ; mem[p]<-acc, acc=0
  w73   01042B01D5   post    (p),(p)-80
  w74   01022501CD   mac     (p),(p)+80
  w75   0212201412   mac     acc,(p)+1 ; mem[p]<-acc, acc=0
  w76   01042AF1D5   post    (p),(p)-81
  w77   01022511CD   mac     (p),(p)+81
  w78   0212201412   mac     acc,(p)+1 ; mem[p]<-acc, acc=0
  w79   01042AE1D5   post    (p),(p)-82
  w80   01022521CD   mac     (p),(p)+82
  w81   0212201412   mac     acc,(p)+1 ; mem[p]<-acc, acc=0
  w82   01042AD1D5   post    (p),(p)-83
  w83   01022531CD   mac     (p),(p)+83
  w84   0212201412   mac     acc,(p)+1 ; mem[p]<-acc, acc=0
  w85   01042AC1D5   post    (p),(p)-84
  w86   01022541CD   mac     (p),(p)+84
  w87   0212201412   mac     acc,(p)+1 ; mem[p]<-acc, acc=0
  w88   01042AB1D5   post    (p),(p)-85
  w89   01022551CD   mac     (p),(p)+85
  w90   0212201412   mac     acc,(p)+1 ; mem[p]<-acc, acc=0
  w91   01042AA1D5   post    (p),(p)-86
  w92   01022561CD   mac     (p),(p)+86
  w93   0212201412   mac     acc,(p)+1 ; mem[p]<-acc, acc=0
  w94   01042A91D5   post    (p),(p)-87
  w95   01022571CD   mac     (p),(p)+87
  w96   0212201412   mac     acc,(p)+1 ; mem[p]<-acc, acc=0
  w97   01042A81D5   post    (p),(p)-88
  w98   01022581CD   mac     (p),(p)+88
  w99   0212AB1412   mac     acc,c+,(p)-79 ; mem[p]<-acc, acc=0
        ; C-RAM[0x0B] (coeff, base 0x00 MEASURED)
        ; coeff C-RAM[0x0B] = filter section cell 1 (role filter, INFERRED)
  w100  0104200000   post.b  (p)0,(p)+0
  w101  0092A00200   mac.b   c,c+,(p)+0 ; store SUPPRESSED (bit7)
        ; C-RAM[0x0C] (coeff, base 0x00 MEASURED)
  w102  00822001C0   mac.b   (p),(p)+0
  w103  0094A00200   wrap    acc,c+          ; acc <- datum(acc) & coef  (LFO modulus)
        ; C-RAM[0x0D] (coeff, base 0x00 MEASURED)
  w104  00002F6447   ?word   0x00002F6447   ; 000.2.F6.447  hi12{-}  [SPECULATIVE (prospective, not measured): SRC 0x11 = ACCB (2nd accumulator)]
  w105  050410E407   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 ph_hle = (dsphle == 5);
	double ph_inc = 0.0, ph_ac = 0.0, ph_ad = 0.0, ph_fb = 0.0;
	if (ph_hle)
	{
		auto q22p = [](u32 v) -> double {
			int32_t s = (v & 0x800000) ? int32_t(v) - 0x1000000 : int32_t(v);
			return double(s) / 4194304.0; };
		const bool bit1 = (dspcfg & 2) != 0;
		const double cs = bit1 ? 1.0 : 2.0;
		const double inc = double(m_dsp1->cram_read(0x00) & 0xffffff) * cs;
		ph_inc = std::clamp(inc * 44100.0 / 8388608.0, 0.02, 12.0) / double(STREAM_RATE);
		ph_ac  = std::clamp(q22p(m_dsp1->cram_read(0x06)) * cs * 0.5, -0.95, 0.95);  // centre a (operand)
		ph_ad  = std::clamp(q22p(m_dsp1->cram_read(0x05)) * cs * 0.5,  0.0, 0.40);   // sweep amplitude
		ph_fb  = std::clamp(q22p(m_dsp1->cram_read(0x02)) * cs * 0.5, -0.95, 0.95);  // feedback (resonance)
	}

Per-sample insert (the reconstructed signal path):

		if (ph_hle)
		{
			m_ph_phase += ph_inc;
			if (m_ph_phase >= 1.0) m_ph_phase -= 1.0;
			const double a = std::clamp(ph_ac + ph_ad * std::sin(2.0 * M_PI * m_ph_phase), -0.98, 0.98);
			for (int i = 0; i < PH_STAGES; i++) { m_ph_l[i].set_a(a); m_ph_r[i].set_a(a); }
			const double sl = double(mix_l) / 32768.0, sr = double(mix_r) / 32768.0;
			double xl = sl + ph_fb * m_ph_fbl, xr = sr + ph_fb * m_ph_fbr;
			for (int i = 0; i < PH_STAGES; i++) { xl = m_ph_l[i].process_one(xl); xr = m_ph_r[i].process_one(xr); }
			m_ph_fbl = xl; m_ph_fbr = xr;
			mix_l = int32_t((0.5 * sl + 0.5 * xl) * 32768.0);
			mix_r = int32_t((0.5 * sr + 0.5 * xr) * 32768.0);
		}