Phaser — HLE + bytecode
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);
}