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 {
66 namespace YM2413NukeYKT {
67 
68 class YM2413 : public YM2413Core
69 {
70 public:
71  YM2413();
72  void reset() override;
73  void writePort(bool port, uint8_t value, int cycle_offset) override;
74  void pokeReg(uint8_t reg, uint8_t value) override;
75  uint8_t peekReg(uint8_t reg) const override;
76  void generateChannels(float* out[9 + 5], uint32_t n) override;
77  float getAmplificationFactor() const override;
78 
79  template<typename Archive>
80  void serialize(Archive& ar, unsigned version);
81 
82  enum class EgState : uint8_t {
83  attack,
84  decay,
85  sustain,
86  release,
87  };
88 
89 private:
90  using bool_2 = std::array<bool, 2>;
91  using int8_t_2 = std::array<int8_t, 2>;
92  using uint8_t_2 = std::array<uint8_t, 2>;
93  struct Patch {
94  constexpr Patch() = default;
95  constexpr Patch(uint8_t tl_, uint8_t dcm_, uint8_t fb_,
96  bool_2 am_, bool_2 vib_, bool_2 et_, bool_2 ksr_, uint8_t_2 multi_,
97  uint8_t_2 ksl_, uint8_t_2 ar_, uint8_t_2 dr_, uint8_t_2 sl_, uint8_t_2 rr_)
98  : dcm(dcm_), vib(vib_), et(et_), sl(sl_) {
99  setTL(tl_);
100  setFB(fb_);
101  setAM(0, am_[0]);
102  setAM(1, am_[1]);
103  setKSR(0, ksr_[0]);
104  setKSR(1, ksr_[1]);
105  setMulti(0, multi_[0]);
106  setMulti(1, multi_[1]);
107  setKSL(0, ksl_[0]);
108  setKSL(1, ksl_[1]);
109  setAR(0, ar_[0]);
110  setAR(1, ar_[1]);
111  setDR(0, dr_[0]);
112  setDR(1, dr_[1]);
113  setRR(0, rr_[0]);
114  setRR(1, rr_[1]);
115  }
116 
117  constexpr void setTL(uint8_t tl) { tl2 = 2 * tl; }
118  constexpr void setFB(uint8_t fb) { fb_t = fb ? (8 - fb) : 31; }
119  constexpr void setAM(int i, bool am) { am_t[i] = am ? -1 : 0; }
120  constexpr void setKSR(int i, bool ksr) { ksr_t[i] = ksr ? 0 : 2; }
121  constexpr void setMulti(int i, uint8_t multi) {
122  constexpr uint8_t PG_MULTI[16] = {
123  1, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 20, 24, 24, 30, 30
124  };
125  multi_t[i] = PG_MULTI[multi];
126  }
127  constexpr void setKSL(int i, uint8_t ksl) { ksl_t[i] = ksl ? (3 - ksl) : 31; }
128  constexpr void setAR (int i, uint8_t ar) { ar4[i] = 4 * ar; }
129  constexpr void setDR (int i, uint8_t dr) { dr4[i] = 4 * dr; }
130  constexpr void setRR (int i, uint8_t rr) { rr4[i] = 4 * rr; }
131 
132  uint8_t tl2 = 0; // multiplied by 2
133  uint8_t dcm = 0;
134  uint8_t fb_t = 31; // 0->31, 1->7, 2->6, .., 6->2, 7->1
135  int8_t_2 am_t = {0, 0}; // false->0, true->-1
136  bool_2 vib = {false, false};
137  bool_2 et = {false, false};
138  uint8_t_2 ksr_t = {2, 2}; // 0->2, 1->0
139  uint8_t_2 multi_t = {1, 1}; // transformed via PG_MULTI[]
140  uint8_t_2 ksl_t = {31, 31}; // 0->31, 1->2, 2->1, 3->0
141  uint8_t_2 ar4 = {0, 0}; // multiplied by 4
142  uint8_t_2 dr4 = {0, 0}; // multiplied by 4
143  uint8_t_2 sl = {0, 0};
144  uint8_t_2 rr4 = {0, 0}; // multiplied by 4
145  };
146  struct Locals {
147  float** out;
148  uint8_t rm_hh_bits;
149  bool use_rm_patches;
150  bool lfo_am_car;
151  bool eg_timer_carry;
152  };
153  struct Write {
154  uint8_t port;
155  uint8_t value;
156 
157  template<typename Archive>
158  void serialize(Archive& ar, unsigned version);
159  };
160 
161 private:
162  template<bool TEST_MODE> NEVER_INLINE void step18(float* out[9 + 5]);
163  template<uint32_t CYCLES, bool TEST_MODE> ALWAYS_INLINE void step(Locals& l);
164 
165  template<uint32_t CYCLES> ALWAYS_INLINE uint32_t phaseCalcIncrement(const Patch& patch1) const;
166  template<uint32_t CYCLES> ALWAYS_INLINE void channelOutput(float* out[9 + 5], int32_t ch_out);
167  template<uint32_t CYCLES> ALWAYS_INLINE const Patch& preparePatch1(bool use_rm_patches) const;
168  template<uint32_t CYCLES, bool TEST_MODE> ALWAYS_INLINE uint32_t getPhase(uint8_t& rm_hh_bits);
169  template<uint32_t CYCLES> ALWAYS_INLINE bool keyOnEvent() const;
170  template<uint32_t CYCLES, bool TEST_MODE> ALWAYS_INLINE void incrementPhase(uint32_t phase_incr, bool prev_rhythm);
171  template<uint32_t CYCLES> ALWAYS_INLINE uint32_t getPhaseMod(uint8_t fb_t);
172  template<uint32_t CYCLES> ALWAYS_INLINE void doOperator(float* out[9 + 5], bool eg_silent);
173  template<uint32_t CYCLES, bool TEST_MODE> ALWAYS_INLINE uint8_t envelopeOutput(uint32_t ksltl, int8_t am_t) const;
174  template<uint32_t CYCLES> ALWAYS_INLINE uint32_t envelopeKSLTL(const Patch& patch1, bool use_rm_patches) const;
175  template<uint32_t CYCLES> ALWAYS_INLINE void envelopeTimer1();
176  template<uint32_t CYCLES, bool TEST_MODE> ALWAYS_INLINE void envelopeTimer2(bool& eg_timer_carry);
177  template<uint32_t CYCLES> ALWAYS_INLINE bool envelopeGenerate1();
178  template<uint32_t CYCLES> ALWAYS_INLINE void envelopeGenerate2(const Patch& patch1, bool use_rm_patches);
179  template<uint32_t CYCLES, bool TEST_MODE> ALWAYS_INLINE void doLFO(bool& lfo_am_car);
180  template<uint32_t CYCLES, bool TEST_MODE> ALWAYS_INLINE void doRhythm();
181  template<uint32_t CYCLES> ALWAYS_INLINE void doRegWrite();
182  template<uint32_t CYCLES> ALWAYS_INLINE void doIO();
183 
184  NEVER_INLINE void doRegWrite(uint32_t channel);
185  void doRegWrite(uint8_t block, uint8_t channel, uint8_t data);
186  NEVER_INLINE void doIO(uint32_t cycles_plus_1, Write& write);
187 
188  void doModeWrite(uint8_t address, uint8_t value);
189  void changeFnumBlock(uint32_t ch);
190 
191 private:
192  static const Patch m_patches[15];
193  static const Patch r_patches[ 6];
194 
195  // IO
196  Write writes[18];
197  uint8_t write_data;
198  uint8_t fm_data;
199  uint8_t write_address;
200  uint8_t write_fm_cycle;
201  bool fast_fm_rewrite;
202  bool test_mode_active;
203 
204  // Envelope generator
205  const uint8_t* attackPtr; // redundant: calculate from eg_timer_shift_lock, eg_timer_lock
206  const uint8_t* releasePtr; // redundant: calculate from eg_timer_shift_lock, eg_timer_lock, eg_counter_state
207  uint32_t eg_timer;
208  uint8_t eg_sl[2];
209  uint8_t eg_out[2];
210  uint8_t eg_counter_state; // 0..3
211  uint8_t eg_timer_shift;
212  uint8_t eg_timer_shift_lock;
213  uint8_t eg_timer_lock;
214  EgState eg_state[18];
215  uint8_t eg_level[18];
216  uint8_t eg_rate[2];
217  bool eg_dokon[18];
218  bool eg_kon[2];
219  bool eg_off[2];
220  bool eg_timer_shift_stop;
221 
222  // Phase generator
223  uint32_t pg_phase[18];
224 
225  // Operator
226  int16_t op_fb1[9];
227  int16_t op_fb2[9];
228  int16_t op_mod;
229  uint16_t op_phase[2];
230 
231  // LFO
232  uint16_t lfo_counter;
233  uint16_t lfo_am_counter;
234  uint8_t lfo_vib_counter;
235  int8_t lfo_vib; // redundant: equal to VIB_TAB[lfo_vib_counter]
236  uint8_t lfo_am_out;
237  bool lfo_am_step;
238  bool lfo_am_dir;
239 
240  // Register set
241  uint16_t fnum[9];
242  uint8_t block[9];
243  uint8_t p_ksl[9]; // redundant: calculate from fnum[] and block[]
244  uint16_t p_incr[9]; // redundant: calculate from fnum[] and block[]
245  uint8_t p_ksr_freq[9]; // redundant: calculate from fnum[] and block[]
246  uint8_t sk_on[9];
247  uint8_t vol8[9]; // multiplied by 8
248  uint8_t inst[9];
249  const Patch* p_inst[9]; // redundant: &patches[inst[]]
250  uint8_t rhythm;
251  uint8_t testmode;
252  Patch patches[1 + 15]; // user patch (modifyable) + 15 ROM patches
253  uint8_t c_dcm[3];
254 
255  // Rhythm mode
256  uint32_t rm_noise;
257  uint8_t rm_tc_bits;
258 
259  int delay6;
260  int delay7;
261  int delay10;
262  int delay11;
263  int delay12;
264 
265  // only used for peekReg();
266  uint8_t regs[64];
267  uint8_t latch;
268 
269  int allowed_offset = 0; // Hack: see comments in writePort()
270 };
271 
272 } // namespace NukeYKT
273 } // namespace openmsx
274 
275 #endif
ALWAYS_INLINE
#define ALWAYS_INLINE
Definition: inline.hh:16
inline.hh
NEVER_INLINE
#define NEVER_INLINE
Definition: inline.hh:17
openmsx::YM2413NukeYKT::YM2413::writePort
void writePort(bool port, uint8_t value, int cycle_offset) override
Write to the YM2413 register/data port.
Definition: YM2413NukeYKT.cc:865
openmsx::YM2413NukeYKT::YM2413::getAmplificationFactor
float getAmplificationFactor() const override
Returns normalization factor.
Definition: YM2413NukeYKT.cc:919
openmsx::YM2413NukeYKT::YM2413::EgState::release
@ release
openmsx::YM2413NukeYKT::YM2413::pokeReg
void pokeReg(uint8_t reg, uint8_t value) override
Write to a YM2413 register (for debug).
Definition: YM2413NukeYKT.cc:902
openmsx::YM2413NukeYKT::YM2413::reset
void reset() override
Reset this YM2413 core.
Definition: YM2413NukeYKT.cc:127
openmsx::YM2413NukeYKT::YM2413::peekReg
uint8_t peekReg(uint8_t reg) const override
Read from a YM2413 register (for debug).
Definition: YM2413NukeYKT.cc:914
openmsx::YM2413Core
Abstract interface for the YM2413 core.
Definition: YM2413Core.hh:27
YM2413Core.hh
openmsx::YM2413NukeYKT::YM2413
Definition: YM2413NukeYKT.hh:69
openmsx::YM2413NukeYKT::YM2413::serialize
void serialize(Archive &ar, unsigned version)
Definition: YM2413NukeYKT.cc:945
openmsx::YM2413NukeYKT::YM2413::EgState::decay
@ decay
openmsx::YM2413NukeYKT::YM2413::EgState::sustain
@ sustain
openmsx::YM2413NukeYKT::YM2413::generateChannels
void generateChannels(float *out[9+5], uint32_t n) override
Definition: YM2413NukeYKT.cc:817
openmsx::YM2413NukeYKT::YM2413::YM2413
YM2413()
Definition: YM2413NukeYKT.cc:118
openmsx::YM2413NukeYKT::YM2413::EgState
EgState
Definition: YM2413NukeYKT.hh:82
openmsx::YM2413NukeYKT::YM2413::EgState::attack
@ attack
openmsx
This file implemented 3 utility functions:
Definition: Autofire.cc:5