openMSX
YM2413NukeYKT.hh
Go to the documentation of this file.
1 /*
2 * Original copyright:
3 * -------------------------------------------------------------------
4 * Copyright (C) 2019 Nuke.YKT
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 *
17 * Yamaha YM2413 emulator
18 * Thanks:
19 * siliconpr0n.org(digshadow, John McMaster):
20 * VRC VII decap and die shot.
21 *
22 * version: 0.9
23 * -------------------------------------------------------------------
24 *
25 * Heavily modified by Wouter Vermaelen for use in openMSX.
26 *
27 * The most important changes are:
28 * - Adapt to the openMSX API (the 'YM2413Core' base-class).
29 * - The original NukeYKT code is probably close to how the real hardware is
30 * implemented, this is very useful while reverse engineering the YM2413. For
31 * example it faithfully implements the YM2413 'pipeline' (how the sound
32 * generation suboperations are spread over time). Though, for use in openMSX,
33 * we're only interested in the final result. So as long as the generated
34 * sound remains 100% identical, we're free to transform the code to a form
35 * that's more suited for an efficient software implementation. Of course
36 * large parts of this pipeline remain in the transformed code because it does
37 * play an important role in for example the exact timing of when register
38 * changes take effect. So all transformations have to be tested very
39 * carefully.
40 * - The current code runs (on average) over 3x faster than the original, while
41 * still generating identical output. This was measured/tested on a large set
42 * of vgm files.
43 * - TODO document better what kind of transformations were done.
44 * * Emulate 18-steps at-a-time.
45 * * Specialize for the common case that testmode==0 (but still have slower
46 * fallback code).
47 * * Move sub-operations in the pipeline (e.g. to eliminate temporary state)
48 * when this doesn't have an observable effect.
49 * * Lots of small tweak.
50 * * ...
51 *
52 * TODO:
53 * - In openMSX the YM2413 is often silent for large periods of time (e.g. maybe
54 * the emulated MSX program doesn't use the YM2413). Can we easily detect an
55 * idle YM2413 and then bypass large parts of the emulation?
56 */
57 
58 #ifndef YM2413NUKEYKT_HH
59 #define YM2413NUKEYKT_HH
60 
61 #include "YM2413Core.hh"
62 #include "inline.hh"
63 #include <array>
64 
65 namespace openmsx::YM2413NukeYKT {
66 
67 class YM2413 final : public YM2413Core
68 {
69 public:
70  YM2413();
71  void reset() override;
72  void writePort(bool port, uint8_t value, int cycle_offset) override;
73  void pokeReg(uint8_t reg, uint8_t value) override;
74  [[nodiscard]] uint8_t peekReg(uint8_t reg) const override;
75  void generateChannels(float* out[9 + 5], uint32_t n) override;
76  [[nodiscard]] float getAmplificationFactor() const override;
77 
78  template<typename Archive>
79  void serialize(Archive& ar, unsigned version);
80 
81  enum class EgState : uint8_t {
82  attack,
83  decay,
84  sustain,
85  release,
86  };
87 
88 private:
89  using bool_2 = std::array<bool, 2>;
90  using int8_t_2 = std::array<int8_t, 2>;
91  using uint8_t_2 = std::array<uint8_t, 2>;
92  struct Patch {
93  constexpr Patch() = default;
94  constexpr Patch(uint8_t tl_, uint8_t dcm_, uint8_t fb_,
95  bool_2 am_, bool_2 vib_, bool_2 et_, bool_2 ksr_, uint8_t_2 multi_,
96  uint8_t_2 ksl_, uint8_t_2 ar_, uint8_t_2 dr_, uint8_t_2 sl_, uint8_t_2 rr_)
97  : dcm(dcm_), vib(vib_), et(et_), sl(sl_) {
98  setTL(tl_);
99  setFB(fb_);
100  setAM(0, am_[0]);
101  setAM(1, am_[1]);
102  setKSR(0, ksr_[0]);
103  setKSR(1, ksr_[1]);
104  setMulti(0, multi_[0]);
105  setMulti(1, multi_[1]);
106  setKSL(0, ksl_[0]);
107  setKSL(1, ksl_[1]);
108  setAR(0, ar_[0]);
109  setAR(1, ar_[1]);
110  setDR(0, dr_[0]);
111  setDR(1, dr_[1]);
112  setRR(0, rr_[0]);
113  setRR(1, rr_[1]);
114  }
115 
116  constexpr void setTL(uint8_t tl) { tl2 = 2 * tl; }
117  constexpr void setFB(uint8_t fb) { fb_t = fb ? (8 - fb) : 31; }
118  constexpr void setAM(int i, bool am) { am_t[i] = am ? -1 : 0; }
119  constexpr void setKSR(int i, bool ksr) { ksr_t[i] = ksr ? 0 : 2; }
120  constexpr void setMulti(int i, uint8_t multi) {
121  constexpr uint8_t PG_MULTI[16] = {
122  1, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 20, 24, 24, 30, 30
123  };
124  multi_t[i] = PG_MULTI[multi];
125  }
126  constexpr void setKSL(int i, uint8_t ksl) { ksl_t[i] = ksl ? (3 - ksl) : 31; }
127  constexpr void setAR (int i, uint8_t ar) { ar4[i] = 4 * ar; }
128  constexpr void setDR (int i, uint8_t dr) { dr4[i] = 4 * dr; }
129  constexpr void setRR (int i, uint8_t rr) { rr4[i] = 4 * rr; }
130 
131  uint8_t tl2 = 0; // multiplied by 2
132  uint8_t dcm = 0;
133  uint8_t fb_t = 31; // 0->31, 1->7, 2->6, .., 6->2, 7->1
134  int8_t_2 am_t = {0, 0}; // false->0, true->-1
135  bool_2 vib = {false, false};
136  bool_2 et = {false, false};
137  uint8_t_2 ksr_t = {2, 2}; // 0->2, 1->0
138  uint8_t_2 multi_t = {1, 1}; // transformed via PG_MULTI[]
139  uint8_t_2 ksl_t = {31, 31}; // 0->31, 1->2, 2->1, 3->0
140  uint8_t_2 ar4 = {0, 0}; // multiplied by 4
141  uint8_t_2 dr4 = {0, 0}; // multiplied by 4
142  uint8_t_2 sl = {0, 0};
143  uint8_t_2 rr4 = {0, 0}; // multiplied by 4
144  };
145  struct Locals {
146  float** out;
147  uint8_t rm_hh_bits;
148  bool use_rm_patches;
149  bool lfo_am_car;
150  bool eg_timer_carry;
151  };
152  struct Write {
153  uint8_t port;
154  uint8_t value;
155 
156  template<typename Archive>
157  void serialize(Archive& ar, unsigned version);
158  };
159 
160 private:
161  template<bool TEST_MODE> NEVER_INLINE void step18(float* out[9 + 5]);
162  template<uint32_t CYCLES, bool TEST_MODE> ALWAYS_INLINE void step(Locals& l);
163 
164  template<uint32_t CYCLES> ALWAYS_INLINE uint32_t phaseCalcIncrement(const Patch& patch1) const;
165  template<uint32_t CYCLES> ALWAYS_INLINE void channelOutput(float* out[9 + 5], int32_t ch_out);
166  template<uint32_t CYCLES> ALWAYS_INLINE const Patch& preparePatch1(bool use_rm_patches) const;
167  template<uint32_t CYCLES, bool TEST_MODE> ALWAYS_INLINE uint32_t getPhase(uint8_t& rm_hh_bits);
168  template<uint32_t CYCLES> ALWAYS_INLINE bool keyOnEvent() const;
169  template<uint32_t CYCLES, bool TEST_MODE> ALWAYS_INLINE void incrementPhase(uint32_t phase_incr, bool prev_rhythm);
170  template<uint32_t CYCLES> ALWAYS_INLINE uint32_t getPhaseMod(uint8_t fb_t);
171  template<uint32_t CYCLES> ALWAYS_INLINE void doOperator(float* out[9 + 5], bool eg_silent);
172  template<uint32_t CYCLES, bool TEST_MODE> ALWAYS_INLINE uint8_t envelopeOutput(uint32_t ksltl, int8_t am_t) const;
173  template<uint32_t CYCLES> ALWAYS_INLINE uint32_t envelopeKSLTL(const Patch& patch1, bool use_rm_patches) const;
174  template<uint32_t CYCLES> ALWAYS_INLINE void envelopeTimer1();
175  template<uint32_t CYCLES, bool TEST_MODE> ALWAYS_INLINE void envelopeTimer2(bool& eg_timer_carry);
176  template<uint32_t CYCLES> ALWAYS_INLINE bool envelopeGenerate1();
177  template<uint32_t CYCLES> ALWAYS_INLINE void envelopeGenerate2(const Patch& patch1, bool use_rm_patches);
178  template<uint32_t CYCLES, bool TEST_MODE> ALWAYS_INLINE void doLFO(bool& lfo_am_car);
179  template<uint32_t CYCLES, bool TEST_MODE> ALWAYS_INLINE void doRhythm();
180  template<uint32_t CYCLES> ALWAYS_INLINE void doRegWrite();
181  template<uint32_t CYCLES> ALWAYS_INLINE void doIO();
182 
183  NEVER_INLINE void doRegWrite(uint32_t channel);
184  void doRegWrite(uint8_t block, uint8_t channel, uint8_t data);
185  NEVER_INLINE void doIO(uint32_t cycles_plus_1, Write& write);
186 
187  void doModeWrite(uint8_t address, uint8_t value);
188  void changeFnumBlock(uint32_t ch);
189 
190 private:
191  static const Patch m_patches[15];
192  static const Patch r_patches[ 6];
193 
194  // IO
195  Write writes[18];
196  uint8_t write_data;
197  uint8_t fm_data;
198  uint8_t write_address;
199  uint8_t write_fm_cycle;
200  bool fast_fm_rewrite;
201  bool test_mode_active;
202 
203  // Envelope generator
204  const uint8_t* attackPtr; // redundant: calculate from eg_timer_shift_lock, eg_timer_lock
205  const uint8_t* releasePtr; // redundant: calculate from eg_timer_shift_lock, eg_timer_lock, eg_counter_state
206  uint32_t eg_timer;
207  uint8_t eg_sl[2];
208  uint8_t eg_out[2];
209  uint8_t eg_counter_state; // 0..3
210  uint8_t eg_timer_shift;
211  uint8_t eg_timer_shift_lock;
212  uint8_t eg_timer_lock;
213  EgState eg_state[18];
214  uint8_t eg_level[18];
215  uint8_t eg_rate[2];
216  bool eg_dokon[18];
217  bool eg_kon[2];
218  bool eg_off[2];
219  bool eg_timer_shift_stop;
220 
221  // Phase generator
222  uint32_t pg_phase[18];
223 
224  // Operator
225  int16_t op_fb1[9];
226  int16_t op_fb2[9];
227  int16_t op_mod;
228  uint16_t op_phase[2];
229 
230  // LFO
231  uint16_t lfo_counter;
232  uint16_t lfo_am_counter;
233  uint8_t lfo_vib_counter;
234  int8_t lfo_vib; // redundant: equal to VIB_TAB[lfo_vib_counter]
235  uint8_t lfo_am_out;
236  bool lfo_am_step;
237  bool lfo_am_dir;
238 
239  // Register set
240  uint16_t fnum[9];
241  uint8_t block[9];
242  uint8_t p_ksl[9]; // redundant: calculate from fnum[] and block[]
243  uint16_t p_incr[9]; // redundant: calculate from fnum[] and block[]
244  uint8_t p_ksr_freq[9]; // redundant: calculate from fnum[] and block[]
245  uint8_t sk_on[9];
246  uint8_t vol8[9]; // multiplied by 8
247  uint8_t inst[9];
248  const Patch* p_inst[9]; // redundant: &patches[inst[]]
249  uint8_t rhythm;
250  uint8_t testmode;
251  Patch patches[1 + 15]; // user patch (modifyable) + 15 ROM patches
252  uint8_t c_dcm[3];
253 
254  // Rhythm mode
255  uint32_t rm_noise;
256  uint8_t rm_tc_bits;
257 
258  int delay6;
259  int delay7;
260  int delay10;
261  int delay11;
262  int delay12;
263 
264  // only used for peekReg();
265  uint8_t regs[64];
266  uint8_t latch;
267 
268  int allowed_offset = 0; // Hack: see comments in writePort()
269 };
270 
271 } // namespace openmsx::NukeYKT
272 
273 #endif
Abstract interface for the YM2413 core.
Definition: YM2413Core.hh:27
uint8_t peekReg(uint8_t reg) const override
Read from a YM2413 register (for debug).
void writePort(bool port, uint8_t value, int cycle_offset) override
Write to the YM2413 register/data port.
float getAmplificationFactor() const override
Returns normalization factor.
void pokeReg(uint8_t reg, uint8_t value) override
Write to a YM2413 register (for debug).
void serialize(Archive &ar, unsigned version)
void reset() override
Reset this YM2413 core.
void generateChannels(float *out[9+5], uint32_t n) override
#define NEVER_INLINE
Definition: inline.hh:17
#define ALWAYS_INLINE
Definition: inline.hh:16