OXYGENHUB
◆ main openmsx / src / sound / YM2413NukeYKT.cc
34.7 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
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
* See YM2413NukeYKT.hh for more info.
26
*/
27
28
#include "YM2413NukeYKT.hh"
29
30
#include "Math.hh"
31
#include "cstd.hh"
32
#include "enumerate.hh"
33
#include "narrow.hh"
34
#include "one_of.hh"
35
#include "ranges.hh"
36
#include "serialize.hh"
37
#include "unreachable.hh"
38
#include "xrange.hh"
39
40
#include <algorithm>
41
#include <array>
42
#include <cstring>
43
44
namespace openmsx {
45
namespace YM2413NukeYKT {
46
47
[[nodiscard]] static constexpr bool is_rm_cycle(int cycle)
48
{
49
	return (11 <= cycle) && (cycle <= 16);
50
}
51
[[nodiscard]] static constexpr YM2413::RmNum rm_for_cycle(int cycle)
52
{
53
	return static_cast<YM2413::RmNum>(cycle - 11);
54
}
55
56
static constexpr auto logSinTab = [] {
57
	std::array<uint16_t, 256> result = {};
58
	//for (auto [i, r] : enumerate(result)) { msvc bug
59
	for (int i = 0; i < 256; ++i) {
60
		result[i] = narrow_cast<uint16_t>(cstd::round(-cstd::log2<8, 3>(cstd::sin<2>((double(i) + 0.5) * Math::pi / 256.0 / 2.0)) * 256.0));
61
	}
62
	return result;
63
}();
64
static constexpr auto expTab = [] {
65
	std::array<uint16_t, 256> result = {};
66
	//for (auto [i, r] : enumerate(result)) { msvc bug
67
	for (int i = 0; i < 256; ++i) {
68
		result[i] = narrow_cast<uint16_t>(cstd::round((cstd::exp2<6>(double(255 - i) / 256.0)) * 1024.0));
69
	}
70
	return result;
71
}();
72
73
constexpr std::array<YM2413::Patch, 15> YM2413::m_patches = {
74
	YM2413::Patch{0x1e, 2, 7, {0, 0}, {1, 1}, {1, 1}, {1, 0}, {0x1, 0x1}, {0, 0}, {0xd, 0x7}, {0x0, 0x8}, {0x0, 0x1}, {0x0, 0x7}},
75
	YM2413::Patch{0x1a, 1, 5, {0, 0}, {0, 1}, {0, 0}, {1, 0}, {0x3, 0x1}, {0, 0}, {0xd, 0xf}, {0x8, 0x7}, {0x2, 0x1}, {0x3, 0x3}},
76
	YM2413::Patch{0x19, 0, 0, {0, 0}, {0, 0}, {0, 0}, {1, 0}, {0x3, 0x1}, {2, 0}, {0xf, 0xc}, {0x2, 0x4}, {0x1, 0x2}, {0x1, 0x3}},
77
	YM2413::Patch{0x0e, 0, 7, {0, 0}, {0, 1}, {1, 1}, {1, 0}, {0x1, 0x1}, {0, 0}, {0xa, 0x6}, {0x8, 0x4}, {0x7, 0x2}, {0x0, 0x7}},
78
	YM2413::Patch{0x1e, 0, 6, {0, 0}, {0, 0}, {1, 1}, {1, 0}, {0x2, 0x1}, {0, 0}, {0xe, 0x7}, {0x0, 0x6}, {0x0, 0x2}, {0x0, 0x8}},
79
	YM2413::Patch{0x16, 0, 5, {0, 0}, {0, 0}, {1, 1}, {1, 0}, {0x1, 0x2}, {0, 0}, {0xe, 0x7}, {0x0, 0x1}, {0x0, 0x1}, {0x0, 0x8}},
80
	YM2413::Patch{0x1d, 0, 7, {0, 0}, {0, 1}, {1, 1}, {0, 0}, {0x1, 0x1}, {0, 0}, {0x8, 0x8}, {0x2, 0x1}, {0x1, 0x0}, {0x0, 0x7}},
81
	YM2413::Patch{0x2d, 2, 4, {0, 0}, {0, 0}, {1, 1}, {0, 0}, {0x3, 0x1}, {0, 0}, {0xa, 0x7}, {0x2, 0x2}, {0x0, 0x0}, {0x0, 0x7}},
82
	YM2413::Patch{0x1b, 0, 6, {0, 0}, {1, 1}, {1, 1}, {0, 0}, {0x1, 0x1}, {0, 0}, {0x6, 0x6}, {0x4, 0x5}, {0x1, 0x1}, {0x0, 0x7}},
83
	YM2413::Patch{0x0b, 3, 0, {0, 0}, {1, 1}, {0, 1}, {0, 0}, {0x1, 0x1}, {0, 0}, {0x8, 0xf}, {0x5, 0x7}, {0x7, 0x0}, {0x1, 0x7}},
84
	YM2413::Patch{0x03, 2, 1, {0, 0}, {0, 0}, {0, 0}, {1, 0}, {0x3, 0x1}, {2, 0}, {0xf, 0xe}, {0xa, 0x4}, {0x1, 0x0}, {0x0, 0x4}},
85
	YM2413::Patch{0x24, 0, 7, {0, 1}, {0, 1}, {0, 0}, {1, 0}, {0x7, 0x1}, {0, 0}, {0xf, 0xf}, {0x8, 0x8}, {0x2, 0x1}, {0x2, 0x2}},
86
	YM2413::Patch{0x0c, 0, 5, {0, 0}, {1, 1}, {1, 0}, {0, 1}, {0x1, 0x0}, {0, 0}, {0xc, 0xf}, {0x2, 0x5}, {0x2, 0x4}, {0x0, 0x2}},
87
	YM2413::Patch{0x15, 0, 3, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0x1, 0x1}, {1, 0}, {0xc, 0x9}, {0x9, 0x5}, {0x0, 0x0}, {0x3, 0x2}},
88
	YM2413::Patch{0x09, 0, 3, {0, 0}, {1, 1}, {1, 0}, {0, 0}, {0x1, 0x1}, {2, 0}, {0xf, 0xe}, {0x1, 0x4}, {0x4, 0x1}, {0x0, 0x3}},
89
};
90
91
constexpr array_with_enum_index<YM2413::RmNum, YM2413::Patch> YM2413::r_patches = {
92
	YM2413::Patch{0x18, 1, 7, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0x1, 0x0}, {0, 0}, {0xd, 0x0}, {0xf, 0x0}, {0x6, 0x0}, {0xa, 0x0}},
93
	YM2413::Patch{0x00, 0, 0, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0x1, 0x0}, {0, 0}, {0xc, 0x0}, {0x8, 0x0}, {0xa, 0x0}, {0x7, 0x0}},
94
	YM2413::Patch{0x00, 0, 0, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0x5, 0x0}, {0, 0}, {0xf, 0x0}, {0x8, 0x0}, {0x5, 0x0}, {0x9, 0x0}},
95
	YM2413::Patch{0x00, 0, 0, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0x0, 0x1}, {0, 0}, {0x0, 0xf}, {0x0, 0x8}, {0x0, 0x6}, {0x0, 0xd}},
96
	YM2413::Patch{0x00, 0, 0, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0x0, 0x1}, {0, 0}, {0x0, 0xd}, {0x0, 0x8}, {0x0, 0x4}, {0x0, 0x8}},
97
	YM2413::Patch{0x00, 0, 0, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0x0, 0x1}, {0, 0}, {0x0, 0xa}, {0x0, 0xa}, {0x0, 0x5}, {0x0, 0x5}},
98
};
99
100
static constexpr std::array<uint8_t, 18> CH_OFFSET = {
101
	1, 2, 0, 1, 2, 3, 4, 5, 3, 4, 5, 6, 7, 8, 6, 7, 8, 0
102
};
103
104
static constexpr std::array<int8_t, 8> VIB_TAB = {0, 1, 2, 1, 0, -1, -2, -1};
105
106
// Compute envelope generator tables at compile time.
107
// These tables control the attack and release characteristics of the YM2413 sound chip.
108
109
using B4 = std::array<bool, 4>;
110
constexpr std::array<B4, 4> EG_STEP_HI = {
111
        B4{false, true, true, true},
112
        B4{false, false, false, true},
113
        B4{false, false, true, true},
114
        B4{false, false, false, false},
115
};
116
117
// Compute the attack envelope table
118
[[nodiscard]] constexpr auto computeAttackTable()
119
{
120
	std::array<std::array<std::array<uint8_t, 64 /*rate*/>, 4 /*timer*/>, 14 /*timer_shift*/> result = {};
121
	for (auto timer_shift : xrange(14)) {
122
		for (auto timer : xrange(4)) {
123
			for (auto rate : xrange(64)) {
124
				uint8_t rate_hi = rate >> 2;
125
				uint8_t rate_lo = rate & 3;
126
				bool inc_hi = EG_STEP_HI[timer][rate_lo];
127
				bool inc_lo = [&]() -> bool {
128
					if ((rate_hi < 12) && (rate_hi != 0)) {
129
						switch (timer_shift + rate_hi) {
130
						case 12: return true;
131
						case 13: return rate_lo & 2;
132
						case 14: return rate_lo & 1;
133
						}
134
					}
135
					return false;
136
				}();
137
				result[timer_shift][timer][rate] = [&]() {
138
					if (rate_hi != 0xf) {
139
						int32_t shift = (rate_hi < 12)
140
						              ? inc_lo
141
						              : (rate_hi - 11 + inc_hi);
142
						if (shift > 0) {
143
							return 5 - shift;
144
						}
145
					}
146
					return 31;
147
				}();
148
			}
149
		}
150
	}
151
	return result;
152
}
153
static constexpr auto attack = computeAttackTable();
154
155
// Compute the uncompressed release envelope table
156
[[nodiscard]] constexpr auto computeReleaseTable()
157
{
158
	std::array<std::array<std::array<std::array<uint8_t, 64 /*rate*/>, 4 /*counter_state*/>, 4 /*timer*/>, 14 /*timer_shift*/> result = {};
159
	for (auto timer_shift : xrange(14)) {
160
		for (auto timer : xrange(4)) {
161
			for (auto rate : xrange(64)) {
162
				uint8_t rate_hi = rate >> 2;
163
				uint8_t rate_lo = rate & 3;
164
				bool inc_hi = EG_STEP_HI[timer][rate_lo];
165
				bool inc_lo = [&]() -> bool {
166
					if ((rate_hi < 12) && (rate_hi != 0)) {
167
						switch (timer_shift + rate_hi) {
168
						case 12: return true;
169
						case 13: return rate_lo & 2;
170
						case 14: return rate_lo & 1;
171
						}
172
					}
173
					return false;
174
				}();
175
				for (auto counter_state : xrange(4)) {
176
					bool i0 = rate_hi == 15 || (rate_hi == 14 && inc_hi);
177
					bool i1 = (rate_hi == 14 && !inc_hi) ||
178
					          (rate_hi == 13 && inc_hi) ||
179
					          (rate_hi == 13 && !inc_hi && (counter_state & 1)) ||
180
					          (rate_hi == 12 && inc_hi && (counter_state & 1)) ||
181
					          (rate_hi == 12 && !inc_hi && (counter_state == 3)) ||
182
					          (inc_lo && (counter_state == 3));
183
					int i01 = (i0 << 1) | i1;
184
					result[timer_shift][timer][counter_state][rate] = i01;
185
				}
186
			}
187
		}
188
	}
189
	return result;
190
}
191
192
// Compress a table by deduplicating identical 64-element arrays
193
template<typename Table>
194
[[nodiscard]] constexpr auto compressTable(const Table& table)
195
{
196
	struct CompressedTable {
197
		std::array<std::array<std::array<uint8_t, 4>, 4>, 14> index;
198
		std::array<std::array<uint8_t, 64>, 64> data;
199
	};
200
	CompressedTable result = {};
201
	size_t out_n = 0;
202
	for (auto i : xrange(14)) {
203
		for (auto j : xrange(4)) {
204
			for (auto k : xrange(4)) {
205
				auto haystack = std::span{result.data.data(), out_n};
206
				auto it = std::ranges::find_if(haystack, [&](const auto& candidate) {
207
					return std::ranges::equal(table[i][j][k], candidate);
208
				});
209
				if (it != haystack.end()) {
210
					result.index[i][j][k] = narrow<uint8_t>(std::ranges::distance(haystack.begin(), it));
211
				} else {
212
					copy_to_range(table[i][j][k], result.data[out_n]);
213
					result.index[i][j][k] = out_n;
214
					++out_n;
215
				}
216
			}
217
		}
218
	}
219
	return result;
220
}
221
static constexpr auto release = compressTable(computeReleaseTable());
222
223
224
YM2413::YM2413()
225
	: attackPtr (/*dummy*/attack[0][0])
226
	, releasePtr(/*dummy*/release.data[0])
227
{
228
	// copy ROM patches to array (for faster lookup)
229
	copy_to_range(m_patches, subspan(patches, 1));
230
	reset();
231
}
232
233
void YM2413::reset()
234
{
235
	for (auto& w : writes) w.port = uint8_t(-1);
236
	write_fm_cycle = uint8_t(-1);
237
	write_data = fm_data = write_address = 0;
238
	fast_fm_rewrite = test_mode_active = false;
239
240
	eg_counter_state = 3;
241
	eg_timer = eg_timer_shift = eg_timer_shift_lock = eg_timer_lock = 0;
242
	attackPtr  = attack[eg_timer_shift_lock][eg_timer_lock];
243
	auto idx = release.index[eg_timer_shift_lock][eg_timer_lock][eg_counter_state];
244
	releasePtr = release.data[idx];
245
	std::ranges::fill(eg_state, EgState::release);
246
	std::ranges::fill(eg_level, 0x7f);
247
	std::ranges::fill(eg_dokon, false);
248
	eg_rate[0] = eg_rate[1] = 0;
249
	eg_sl[0] = eg_sl[1] = eg_out[0] = eg_out[1] = 0;
250
	eg_timer_shift_stop = false;
251
	eg_kon[0] = eg_kon[1] = eg_off[0] = eg_off[1] = false;
252
253
	std::ranges::fill(pg_phase, 0);
254
	std::ranges::fill(op_fb1, 0);
255
	std::ranges::fill(op_fb2, 0);
256
257
	op_mod = 0;
258
	op_phase[0] = op_phase[1] = 0;
259
260
	lfo_counter = lfo_am_counter = 0;
261
	lfo_vib_counter = lfo_am_out = 0;
262
	lfo_vib = VIB_TAB[lfo_vib_counter];
263
	lfo_am_step = lfo_am_dir = false;
264
265
	std::ranges::fill(fnum, 0);
266
	std::ranges::fill(block, 0);
267
	std::ranges::fill(vol8, 0);
268
	std::ranges::fill(inst, 0);
269
	std::ranges::fill(sk_on, 0);
270
	for (auto i : xrange(9)) {
271
		p_inst[i] = &patches[inst[i]];
272
		changeFnumBlock(i);
273
	}
274
	rhythm = testMode = 0;
275
	patches[0] = Patch(); // reset user patch, leave ROM patches alone
276
	c_dcm[0] = c_dcm[1] = c_dcm[2] = 0;
277
278
	rm_noise = 1; // any value except 0 (1 keeps the output the same as the original code)
279
	rm_tc_bits = 0;
280
281
	delay6 = delay7 = delay10 = delay11 = delay12 = 0;
282
283
	std::ranges::fill(regs, 0);
284
	latch = 0;
285
}
286
287
template<uint32_t CYCLES> ALWAYS_INLINE const YM2413::Patch& YM2413::preparePatch1(bool use_rm_patches) const
288
{
289
	return (is_rm_cycle(CYCLES) && use_rm_patches)
290
	     ? r_patches[rm_for_cycle(CYCLES)]
291
	     : *p_inst[CH_OFFSET[CYCLES]];
292
}
293
294
template<uint32_t CYCLES> ALWAYS_INLINE uint32_t YM2413::envelopeKSLTL(const Patch& patch1, bool use_rm_patches) const
295
{
296
	constexpr uint32_t mcsel = ((CYCLES + 1) / 3) & 1;
297
	constexpr uint32_t ch = CH_OFFSET[CYCLES];
298
299
	auto ksl = uint32_t(p_ksl[ch]) >> patch1.ksl_t[mcsel];
300
	auto tl2 = [&] -> uint32_t {
301
		if ((rm_for_cycle(CYCLES) == one_of(RmNum::hh, RmNum::tom)) && use_rm_patches) {
302
			return inst[ch] << (2 + 1);
303
		} else if /*constexpr*/ (mcsel == 1) { // constexpr triggers compile error on visual studio
304
			return vol8[ch];
305
		} else {
306
			return patch1.tl2;
307
		}
308
	}();
309
	return ksl + tl2;
310
}
311
312
template<uint32_t CYCLES> ALWAYS_INLINE void YM2413::envelopeTimer1()
313
{
314
	if constexpr (CYCLES == 0) {
315
		eg_counter_state = (eg_counter_state + 1) & 3;
316
		auto idx = release.index[eg_timer_shift_lock][eg_timer_lock][eg_counter_state];
317
		releasePtr = release.data[idx];
318
	}
319
}
320
321
template<uint32_t CYCLES, bool TEST_MODE> ALWAYS_INLINE void YM2413::envelopeTimer2(bool& eg_timer_carry)
322
{
323
	if constexpr (TEST_MODE) {
324
		if (CYCLES == 0 && (eg_counter_state & 1) == 0) {
325
			eg_timer_lock = eg_timer & 3;
326
			eg_timer_shift_lock = /*likely*/(eg_timer_shift <= 13) ? eg_timer_shift : 0;
327
			eg_timer_shift = 0;
328
329
			attackPtr  = attack[eg_timer_shift_lock][eg_timer_lock];
330
			auto idx = release.index[eg_timer_shift_lock][eg_timer_lock][eg_counter_state];
331
			releasePtr = release.data[idx];
332
		}
333
		{ // EG timer
334
			bool timer_inc = (eg_counter_state != 3) ? false
335
				       : (CYCLES == 0)           ? true
336
								 : eg_timer_carry;
337
			auto timer_bit = (eg_timer & 1) + timer_inc;
338
			eg_timer_carry = timer_bit & 2;
339
			eg_timer = ((timer_bit & 1) << 17) | (eg_timer >> 1);
340
			if (testMode & 8) {
341
				const auto& write = writes[CYCLES];
342
				auto data = (write.port != uint8_t(-1)) ? write.value : write_data;
343
				eg_timer &= 0x2ffff;
344
				eg_timer |= (data << (16 - 2)) & 0x10000;
345
			}
346
		}
347
		if constexpr (CYCLES == 0) {
348
			eg_timer_shift_stop = false;
349
		} else if (!eg_timer_shift_stop && ((eg_timer >> 16) & 1)) {
350
			eg_timer_shift = CYCLES;
351
			eg_timer_shift_stop = true;
352
		}
353
	} else {
354
		if constexpr (CYCLES == 0) {
355
			if ((eg_counter_state & 1) == 0) {
356
				eg_timer_lock = eg_timer & 3;
357
				eg_timer_shift_lock = (eg_timer_shift > 13) ? 0 : eg_timer_shift;
358
359
				attackPtr  = attack[eg_timer_shift_lock][eg_timer_lock];
360
				auto idx = release.index[eg_timer_shift_lock][eg_timer_lock][eg_counter_state];
361
				releasePtr = release.data[idx];
362
			}
363
			if (eg_counter_state == 3) {
364
				eg_timer = (eg_timer + 1) & 0x3ffff;
365
				eg_timer_shift = narrow_cast<uint8_t>(Math::findFirstSet(eg_timer));
366
			}
367
		}
368
	}
369
}
370
371
template<uint32_t CYCLES> ALWAYS_INLINE bool YM2413::envelopeGenerate1()
372
{
373
	int32_t level = eg_level[(CYCLES + 16) % 18];
374
	bool prev2_eg_off   = eg_off[(CYCLES + 16) & 1];
375
	bool prev2_eg_kon   = eg_kon[(CYCLES + 16) & 1];
376
	bool prev2_eg_dokon = eg_dokon[(CYCLES + 16) % 18];
377
378
	using enum EgState;
379
	auto state = eg_state[(CYCLES + 16) % 18];
380
	if (prev2_eg_dokon) [[unlikely]] {
381
		eg_state[(CYCLES + 16) % 18] = attack;
382
	} else if (!prev2_eg_kon) {
383
		eg_state[(CYCLES + 16) % 18] = release;
384
	} else if ((state == attack) && (level == 0)) [[unlikely]] {
385
		eg_state[(CYCLES + 16) % 18] = decay;
386
	} else if ((state == decay) && ((level >> 3) == eg_sl[CYCLES & 1])) [[unlikely]] {
387
		eg_state[(CYCLES + 16) % 18] = sustain;
388
	}
389
390
	auto prev2_rate = eg_rate[(CYCLES + 16) & 1];
391
	int32_t next_level
392
	    = (state != attack && prev2_eg_off && !prev2_eg_dokon) ? 0x7f
393
	    : ((prev2_rate >= 60) && prev2_eg_dokon)                        ? 0x00
394
	                                                                    : level;
395
	auto step = [&] -> int {
396
		switch (state) {
397
		case attack:
398
			if (prev2_eg_kon && (level != 0)) [[likely]] {
399
				return (level ^ 0xfff) >> attackPtr[prev2_rate];
400
			}
401
			break;
402
		case decay:
403
			if ((level >> 3) == eg_sl[CYCLES & 1]) return 0;
404
			[[fallthrough]];
405
		case sustain:
406
		case release:
407
			if (!prev2_eg_off && !prev2_eg_dokon) {
408
				return releasePtr[prev2_rate];
409
			}
410
			break;
411
		default:
412
			UNREACHABLE;
413
		}
414
		return 0;
415
	}();
416
	eg_level[(CYCLES + 16) % 18] = narrow_cast<uint8_t>(next_level + step);
417
418
	return level == 0x7f; // eg_silent
419
}
420
421
template<uint32_t CYCLES> ALWAYS_INLINE void YM2413::envelopeGenerate2(const Patch& patch1, bool use_rm_patches)
422
{
423
	constexpr uint32_t mcsel = ((CYCLES + 1) / 3) & 1;
424
	constexpr uint32_t ch = CH_OFFSET[CYCLES];
425
426
	bool new_eg_off = eg_level[CYCLES] >= 124; // (eg_level[CYCLES] >> 2) == 0x1f;
427
	eg_off[CYCLES & 1] = new_eg_off;
428
429
	auto sk = sk_on[CH_OFFSET[CYCLES]];
430
	bool new_eg_kon = [&] {
431
		bool result = sk & 1;
432
		if (is_rm_cycle(CYCLES) && use_rm_patches) {
433
			switch (rm_for_cycle(CYCLES)) {
434
				using enum RmNum;
435
				case bd0:
436
				case bd1:
437
					result |= bool(rhythm & 0x10);
438
					break;
439
				case sd:
440
					result |= bool(rhythm & 0x08);
441
					break;
442
				case tom:
443
					result |= bool(rhythm & 0x04);
444
					break;
445
				case tc:
446
					result |= bool(rhythm & 0x02);
447
					break;
448
				case hh:
449
					result |= bool(rhythm & 0x01);
450
					break;
451
				default:
452
					break; // suppress warning
453
			}
454
		}
455
		return result;
456
	}();
457
	eg_kon[CYCLES & 1] = new_eg_kon;
458
459
	// Calculate rate
460
	using enum EgState;
461
	auto state_rate = eg_state[CYCLES];
462
	if (state_rate == release && new_eg_kon && new_eg_off) [[unlikely]] {
463
		state_rate = attack;
464
		eg_dokon[CYCLES] = true;
465
	} else {
466
		eg_dokon[CYCLES] = false;
467
	}
468
469
	auto rate4 = [&] {
470
		if (!new_eg_kon && !(sk & 2) && mcsel == 1 && !patch1.et[mcsel]) {
471
			return 7 * 4;
472
		}
473
		if (new_eg_kon && eg_state[CYCLES] == release && !new_eg_off) {
474
			return 12 * 4;
475
		}
476
		bool tom_or_hh = (rm_for_cycle(CYCLES) == one_of(RmNum::tom, RmNum::hh)) && use_rm_patches;
477
		if (!new_eg_kon && !mcsel && !tom_or_hh) {
478
			return 0 * 4;
479
		}
480
		return (state_rate == attack ) ?   patch1.ar4[mcsel]
481
		   :   (state_rate == decay  ) ?   patch1.dr4[mcsel]
482
		   :   (state_rate == sustain) ?   (patch1.et[mcsel] ? (0 * 4) : patch1.rr4[mcsel])
483
		   : /*(state_rate == release) ?*/ ((sk & 2) ? (5 * 4) : patch1.rr4[mcsel]);
484
	}();
485
486
	eg_rate[CYCLES & 1] = narrow_cast<uint8_t>([&] {
487
		if (rate4 == 0) return 0;
488
		auto tmp = rate4 + (p_ksr_freq[ch] >> patch1.ksr_t[mcsel]);
489
		return (tmp < 0x40) ? tmp
490
		                    : (0x3c | (tmp & 3));
491
	}());
492
}
493
494
template<uint32_t CYCLES, bool TEST_MODE> ALWAYS_INLINE void YM2413::doLFO(bool& lfo_am_car)
495
{
496
	if constexpr (TEST_MODE) {
497
		// Update counter
498
		if constexpr (CYCLES == 17) {
499
			lfo_counter++;
500
			if (((lfo_counter & 0x3ff) == 0) || (testMode & 8)) {
501
				lfo_vib_counter = (lfo_vib_counter + 1) & 7;
502
				lfo_vib = VIB_TAB[lfo_vib_counter];
503
			}
504
			lfo_am_step = (lfo_counter & 0x3f) == 0;
505
		}
506
507
		// LFO AM
508
		auto am_inc = ((lfo_am_step || (testMode & 8)) && CYCLES < 9)
509
		            ? (lfo_am_dir | (CYCLES == 0))
510
		            : 0;
511
512
		if constexpr (CYCLES == 0) {
513
			if (lfo_am_dir && (lfo_am_counter & 0x7f) == 0) {
514
				lfo_am_dir = false;
515
			} else if (!lfo_am_dir && (lfo_am_counter & 0x69) == 0x69) {
516
				lfo_am_dir = true;
517
			}
518
		}
519
520
		auto am_bit = (lfo_am_counter & 1) + am_inc + ((CYCLES < 9) ? lfo_am_car : false);
521
		if constexpr (CYCLES < 8) {
522
			lfo_am_car = am_bit & 2;
523
		}
524
		lfo_am_counter = uint16_t(((am_bit & 1) << 8) | (lfo_am_counter >> 1));
525
526
		// Reset LFO
527
		if (testMode & 2) {
528
			lfo_vib_counter = 0;
529
			lfo_vib = VIB_TAB[lfo_vib_counter];
530
			lfo_counter = 0;
531
			lfo_am_dir = false;
532
			lfo_am_counter &= 0xff;
533
		}
534
	} else {
535
		if constexpr (CYCLES == 17) {
536
			int delta = lfo_am_dir ? -1 : 1;
537
538
			if (lfo_am_dir && (lfo_am_counter & 0x7f) == 0) {
539
				lfo_am_dir = false;
540
			} else if (!lfo_am_dir && (lfo_am_counter & 0x69) == 0x69) {
541
				lfo_am_dir = true;
542
			}
543
544
			if (lfo_am_step) {
545
				lfo_am_counter = (lfo_am_counter + delta) & 0x1ff;
546
			}
547
548
			lfo_counter++;
549
			if ((lfo_counter & 0x3ff) == 0) {
550
				lfo_vib_counter = (lfo_vib_counter + 1) & 7;
551
				lfo_vib = VIB_TAB[lfo_vib_counter];
552
			}
553
			lfo_am_step = (lfo_counter & 0x3f) == 0;
554
		}
555
	}
556
	if constexpr (CYCLES == 17) {
557
		lfo_am_out = (lfo_am_counter >> 3) & 0x0f;
558
	}
559
}
560
561
template<uint32_t CYCLES, bool TEST_MODE> ALWAYS_INLINE void YM2413::doRhythm()
562
{
563
	if constexpr (TEST_MODE) {
564
		bool nbit = (rm_noise ^ (rm_noise >> 14)) & 1;
565
		nbit |= bool(testMode & 2);
566
		rm_noise = (nbit << 22) | (rm_noise >> 1);
567
	} else {
568
		// When test-mode does not interfere, the formula for a single step is:
569
		//    x = ((x & 1) << 22) ^ ((x & 0x4000) << 8) ^ (x >> 1);
570
		// From this we can easily derive the value of the noise bit (bit 0)
571
		// 13 and 16 steps in the future (see getPhase()). We can also
572
		// derive a formula that takes 18 steps at once.
573
		if constexpr (CYCLES == 17) {
574
			rm_noise = ((rm_noise & 0x1ff) << 14)
575
			         ^ ((rm_noise & 0x3ffff) << 5)
576
			         ^ (rm_noise & 0x7fc000)
577
			         ^ ((rm_noise >> 9) & 0x3fe0)
578
			         ^ (rm_noise >> 18);
579
		}
580
	}
581
}
582
583
template<uint32_t CYCLES> ALWAYS_INLINE uint32_t YM2413::getPhaseMod(uint8_t fb_t)
584
{
585
	bool ismod2 = ((rhythm & 0x20) && (CYCLES == one_of(12u, 13u)))
586
	            ? false
587
	            : (((CYCLES + 4) / 3) & 1);
588
	bool ismod3 = ((rhythm & 0x20) && (CYCLES == one_of(15u, 16u)))
589
	            ? false
590
	            : (((CYCLES + 1) / 3) & 1);
591
592
	if (ismod3) return op_mod << 1;
593
	if (ismod2) {
594
		constexpr uint32_t cycles9 = (CYCLES + 3) % 9;
595
		uint32_t op_fbsum = (op_fb1[cycles9] + op_fb2[cycles9]) & 0x7fffffff;
596
		return op_fbsum >> fb_t;
597
	}
598
	return 0;
599
}
600
601
template<uint32_t CYCLES> ALWAYS_INLINE void YM2413::doRegWrite()
602
{
603
	if (write_fm_cycle == CYCLES) [[unlikely]] {
604
		doRegWrite(CYCLES % 9);
605
	}
606
}
607
608
void YM2413::changeFnumBlock(uint32_t ch)
609
{
610
	static constexpr std::array<uint8_t, 16> KSL_TABLE = {
611
		0, 32, 40, 45, 48, 51, 53, 55, 56, 58, 59, 60, 61, 62, 63, 64
612
	};
613
	p_ksl[ch] = narrow_cast<uint8_t>(std::max(0, KSL_TABLE[fnum[ch] >> 5] - ((8 - block[ch]) << 3)) << 1);
614
	p_incr[ch] = narrow_cast<uint16_t>(fnum[ch] << block[ch]);
615
	p_ksr_freq[ch] = uint8_t((block[ch] << 1) | (fnum[ch] >> 8));
616
}
617
618
NEVER_INLINE void YM2413::doRegWrite(uint8_t channel)
619
{
620
	if (write_address < 0x40) {
621
		write_fm_cycle = uint8_t(-1);
622
		doRegWrite(write_address & 0xf0, channel, fm_data);
623
	} else {
624
		write_address -= 0x40; // try again in 18 steps
625
	}
626
}
627
628
void YM2413::doRegWrite(uint8_t regBlock, uint8_t channel, uint8_t data)
629
{
630
	switch (regBlock) {
631
	case 0x10:
632
		fnum[channel] = uint16_t((fnum[channel] & 0x100) | data);
633
		changeFnumBlock(channel);
634
		break;
635
	case 0x20:
636
		fnum[channel] = uint16_t((fnum[channel] & 0xff) | ((data & 1) << 8));
637
		block[channel] = (data >> 1) & 7;
638
		changeFnumBlock(channel);
639
		sk_on[channel] = (data >> 4) & 3;
640
		break;
641
	case 0x30:
642
		vol8[channel] = uint8_t((data & 0x0f) << (2 + 1)); // pre-multiply by 8
643
		inst[channel] = (data >> 4) & 0x0f;
644
		p_inst[channel] = &patches[inst[channel]];
645
		break;
646
	}
647
}
648
649
template<uint32_t CYCLES> ALWAYS_INLINE void YM2413::doIO()
650
{
651
	if (writes[CYCLES].port != uint8_t(-1)) [[unlikely]] {
652
		doIO((CYCLES + 1) % 18, writes[CYCLES]);
653
	}
654
}
655
656
NEVER_INLINE void YM2413::doIO(uint32_t cycles_plus_1, Write& write)
657
{
658
	write_data = write.value;
659
	if (write.port) {
660
		// data
661
		if (write_address < 0x10) {
662
			doModeWrite(write_address, write.value);
663
		} else if (write_address < 0x40) {
664
			write_fm_cycle = write_address & 0xf;
665
			fm_data = write.value;
666
			if (!fast_fm_rewrite && (write_fm_cycle == cycles_plus_1)) {
667
				write_address += 0x40; // postpone for 18 steps
668
			}
669
			// First fm-register write takes one cycle longer than
670
			// subsequent writes to the same register. When the address
671
			// is changed (even if to the same value) then there's again
672
			// one cycle penalty.
673
			fast_fm_rewrite = true;
674
		}
675
	} else {
676
		// address
677
		write_address = write.value;
678
		write_fm_cycle = uint8_t(-1); // cancel pending fm write
679
		fast_fm_rewrite = false;
680
	}
681
682
	write.port = uint8_t(-1);
683
}
684
685
void YM2413::doModeWrite(uint8_t address, uint8_t value)
686
{
687
	auto slot = address & 1;
688
	switch (address) {
689
	case 0x00:
690
	case 0x01:
691
		patches[0].setMulti(slot, value & 0x0f);
692
		patches[0].setKSR(slot, (value >> 4) & 1);
693
		patches[0].et[slot]  = (value >> 5) & 1;
694
		patches[0].vib[slot] = (value >> 6) & 1;
695
		patches[0].setAM(slot, (value >> 7) & 1);
696
		break;
697
698
	case 0x02:
699
		patches[0].setKSL(0, (value >> 6) & 3);
700
		patches[0].setTL(value & 0x3f);
701
		break;
702
703
	case 0x03:
704
		patches[0].setKSL(1, (value >> 6) & 3);
705
		patches[0].dcm = (value >> 3) & 3;
706
		patches[0].setFB(value & 7);
707
		break;
708
709
	case 0x04:
710
	case 0x05:
711
		patches[0].setDR(slot, value & 0x0f);
712
		patches[0].setAR(slot, (value >> 4) & 0x0f);
713
		break;
714
715
	case 0x06:
716
	case 0x07:
717
		patches[0].setRR(slot, value & 0x0f);
718
		patches[0].sl[slot] = (value >> 4) & 0x0f;
719
		break;
720
721
	case 0x0e:
722
		rhythm = value & 0x3f;
723
		break;
724
725
	case 0x0f:
726
		testMode = value & 0x0f;
727
		break;
728
	}
729
}
730
731
template<uint32_t CYCLES> ALWAYS_INLINE void YM2413::doOperator(std::span<float*, 9 + 5> out, bool eg_silent)
732
{
733
	bool ismod1 = ((rhythm & 0x20) && (CYCLES == one_of(14u, 15u)))
734
	            ? false
735
	            : (((CYCLES + 2) / 3) & 1);
736
	constexpr bool is_next_mod3 = ((CYCLES + 2) / 3) & 1; // approximate: will 'ismod3' possibly be true next step
737
738
	auto output = [&] -> int32_t {
739
		if (eg_silent) return 0;
740
		auto prev2_phase = op_phase[(CYCLES - 2) & 1];
741
		auto quarter = narrow_cast<uint8_t>((prev2_phase & 0x100) ? ~prev2_phase : prev2_phase);
742
		auto logSin = logSinTab[quarter];
743
		auto op_level = std::min(4095, logSin + (eg_out[(CYCLES - 2) & 1] << 4));
744
		uint32_t op_exp_m = expTab[op_level & 0xff];
745
		auto op_exp_s = op_level >> 8;
746
		if (prev2_phase & 0x200) {
747
			return /*unlikely*/(c_dcm[(CYCLES + 16) % 3] & (ismod1 ? 1 : 2))
748
			       ? ~0
749
			       : ~(op_exp_m >> op_exp_s);
750
		} else {
751
			return narrow<int32_t>(op_exp_m >> op_exp_s);
752
		}
753
	}();
754
755
	if (ismod1) {
756
		constexpr uint32_t cycles9 = (CYCLES + 1) % 9;
757
		op_fb2[cycles9] = op_fb1[cycles9];
758
		op_fb1[cycles9] = narrow_cast<int16_t>(output);
759
	}
760
	channelOutput<CYCLES>(out, output >> 3);
761
762
	if (is_next_mod3) {
763
		op_mod = output & 0x1ff;
764
	}
765
}
766
767
template<uint32_t CYCLES, bool TEST_MODE> ALWAYS_INLINE uint32_t YM2413::getPhase(uint8_t& rm_hh_bits)
768
{
769
	uint32_t phase = pg_phase[CYCLES];
770
771
	// Rhythm mode
772
	if constexpr (CYCLES == 12) {
773
		rm_hh_bits = narrow_cast<uint8_t>(phase >> (2 + 9));
774
	} else if (CYCLES == 16 && (rhythm & 0x20)) {
775
		rm_tc_bits = narrow_cast<uint8_t>(phase >> 8);
776
	}
777
778
	if (rhythm & 0x20) {
779
		auto rm_bit = [&] {
780
			bool rm_hh_bit2 = (rm_hh_bits >> (2 - 2)) & 1;
781
			bool rm_hh_bit3 = (rm_hh_bits >> (3 - 2)) & 1;
782
			bool rm_hh_bit7 = (rm_hh_bits >> (7 - 2)) & 1;
783
			bool rm_tc_bit3 = (rm_tc_bits >> (3 + 9 - 8)) & 1;
784
			bool rm_tc_bit5 = (rm_tc_bits >> (5 + 9 - 8)) & 1;
785
			return (rm_hh_bit2 ^ rm_hh_bit7)
786
			     | (rm_hh_bit3 ^ rm_tc_bit5)
787
			     | (rm_tc_bit3 ^ rm_tc_bit5);
788
		};
789
		auto noise_bit = [&] {
790
			// see comments in doRhythm()
791
			return (rm_noise >> (TEST_MODE ? 0 : (CYCLES + 1))) & 1;
792
		};
793
		switch (CYCLES) {
794
		case 12: { // HH
795
			auto b = rm_bit();
796
			return (b << 9) | ((b ^ noise_bit()) ? 0xd0 : 0x34);
797
		}
798
		case 15: { // SD
799
			auto rm_hh_bit8 = (rm_hh_bits >> (8 - 2)) & 1;
800
			return (rm_hh_bit8 << 9) | ((rm_hh_bit8 ^ noise_bit()) << 8);
801
		}
802
		case 16: // TC
803
			return (rm_bit() << 9) | 0x100;
804
		default:
805
			break; // suppress warning
806
		}
807
	}
808
	return phase >> 9;
809
}
810
811
template<uint32_t CYCLES> ALWAYS_INLINE uint32_t YM2413::phaseCalcIncrement(const Patch& patch1) const
812
{
813
	constexpr uint32_t mcsel = ((CYCLES + 1) / 3) & 1;
814
	constexpr uint32_t ch = CH_OFFSET[CYCLES];
815
816
	uint32_t incr = [&] {
817
		// Apply vibrato?
818
		if (patch1.vib[mcsel]) {
819
			// note: _must_ be '/ 256' rather than '>> 8' because of
820
			// round-to-zero for negative values.
821
			uint32_t freq = fnum[ch] << 1;
822
			freq += (int(freq) * lfo_vib) / 256;
823
			return (freq << block[ch]) >> 1;
824
		} else {
825
			return uint32_t(p_incr[ch]);
826
		}
827
	}();
828
	return (incr * patch1.multi_t[mcsel]) >> 1;
829
}
830
831
template<uint32_t CYCLES> ALWAYS_INLINE bool YM2413::keyOnEvent() const
832
{
833
	bool ismod = ((rhythm & 0x20) && (CYCLES == one_of(12u, 13u)))
834
	           ? false
835
	           : (((CYCLES + 4) / 3) & 1);
836
	return ismod ? eg_dokon[(CYCLES + 3) % 18]
837
	             : eg_dokon[CYCLES];
838
}
839
840
template<uint32_t CYCLES, bool TEST_MODE> ALWAYS_INLINE void YM2413::incrementPhase(uint32_t phase_incr, bool key_on_event)
841
{
842
	uint32_t pg_phase_next = ((TEST_MODE && (testMode & 4)) || key_on_event)
843
	              ? 0
844
	              : pg_phase[CYCLES];
845
	pg_phase[CYCLES] = pg_phase_next + phase_incr;
846
}
847
848
template<uint32_t CYCLES> ALWAYS_INLINE void YM2413::channelOutput(std::span<float*, 9 + 5> out, int32_t ch_out)
849
{
850
	auto outF = narrow_cast<float>(ch_out);
851
	switch (CYCLES) {
852
		case  4: *out[ 0]++ += outF; break;
853
		case  5: *out[ 1]++ += outF; break;
854
		case  6: *out[ 2]++ += outF; break;
855
		case 10: *out[ 3]++ += outF; break;
856
		case 11: *out[ 4]++ += outF; break;
857
		case 12: *out[ 5]++ += outF; break;
858
		case 14: *out[ 9]++ += (rhythm & 0x20) ? 2.0f * outF : 0.0f; break;
859
		case 15: *out[10]++ += narrow_cast<float>(delay10);
860
		         delay10     = (rhythm & 0x20) ? 2 * ch_out : 0; break;
861
		case 16: *out[ 6]++ += narrow_cast<float>(delay6);
862
			 *out[11]++ += narrow_cast<float>(delay11);
863
		         delay6      = (rhythm & 0x20) ? 0 : ch_out;
864
			 delay11     = (rhythm & 0x20) ? 2 * ch_out : 0; break;
865
		case 17: *out[ 7]++ += narrow_cast<float>(delay7);
866
			 *out[12]++ += narrow_cast<float>(delay12);
867
		         delay7      = (rhythm & 0x20) ? 0 : ch_out;
868
			 delay12     = (rhythm & 0x20) ? 2 * ch_out : 0; break;
869
		case  0: *out[ 8]++ += (rhythm & 0x20) ? 0.0f : outF;
870
			 *out[13]++ += (rhythm & 0x20) ? 2.0f * outF : 0.0f; break;
871
		default:
872
			break; // suppress warning
873
	}
874
}
875
876
template<uint32_t CYCLES, bool TEST_MODE> ALWAYS_INLINE uint8_t YM2413::envelopeOutput(uint32_t ksltl, int8_t am_t) const
877
{
878
	if (TEST_MODE && (testMode & 1)) {
879
		return 0;
880
	}
881
	int32_t level = eg_level[CYCLES] + ksltl + (am_t & lfo_am_out);
882
	return narrow_cast<uint8_t>(std::min(127, level));
883
}
884
885
template<uint32_t CYCLES, bool TEST_MODE>
886
ALWAYS_INLINE void YM2413::step(Locals& l)
887
{
888
	if constexpr (CYCLES == 11) {
889
		// the value for 'use_rm_patches' is only meaningful in cycles 11..16
890
		// and it remains constant during that time.
891
		l.use_rm_patches = rhythm & 0x20;
892
	}
893
	const Patch& patch1 = preparePatch1<CYCLES>(l.use_rm_patches);
894
	uint32_t ksltl = envelopeKSLTL<CYCLES>(patch1, l.use_rm_patches);
895
	envelopeTimer1<CYCLES>();
896
	bool eg_silent = envelopeGenerate1<CYCLES>();
897
	envelopeTimer2<CYCLES, TEST_MODE>(l.eg_timer_carry);
898
	envelopeGenerate2<CYCLES>(patch1, l.use_rm_patches);
899
	bool key_on_event = keyOnEvent<CYCLES>();
900
901
	doLFO<CYCLES, TEST_MODE>(l.lfo_am_car);
902
	doRhythm<CYCLES, TEST_MODE>();
903
	uint32_t phaseMod = getPhaseMod<CYCLES>(patch1.fb_t);
904
905
	constexpr uint32_t mcsel = ((CYCLES + 1) / 3) & 1;
906
	eg_sl[CYCLES & 1] = patch1.sl[mcsel];
907
	auto patch2_am_t = patch1.am_t[mcsel];
908
909
	uint32_t phase_incr = phaseCalcIncrement<CYCLES>(patch1);
910
	c_dcm[CYCLES % 3] = patch1.dcm;
911
912
	doRegWrite<CYCLES>();
913
	doIO<CYCLES>();
914
915
	doOperator<CYCLES>(l.out, eg_silent);
916
917
	uint32_t pg_out = getPhase<CYCLES, TEST_MODE>(l.rm_hh_bits);
918
	op_phase[CYCLES & 1] = narrow_cast<uint16_t>(phaseMod + pg_out);
919
	incrementPhase<CYCLES, TEST_MODE>(phase_incr, key_on_event);
920
921
	eg_out[CYCLES & 1] = envelopeOutput<CYCLES, TEST_MODE>(ksltl, patch2_am_t);
922
}
923
924
void YM2413::generateChannels(std::span<float*, 9 + 5> out_, uint32_t n)
925
{
926
	std::array<float*, 9 + 5> out;
927
	copy_to_range(out_, out);
928
929
	// Loop here (instead of in step18) seems faster. (why?)
930
	if (test_mode_active) [[unlikely]] {
931
		repeat(n, [&] { step18<true >(out); });
932
	} else {
933
		repeat(n, [&] { step18<false>(out); });
934
	}
935
	test_mode_active = testMode;
936
}
937
938
template<bool TEST_MODE>
939
NEVER_INLINE void YM2413::step18(std::span<float*, 9 + 5> out)
940
{
941
	Locals l(out);
942
943
	step< 0, TEST_MODE>(l);
944
	step< 1, TEST_MODE>(l);
945
	step< 2, TEST_MODE>(l);
946
	step< 3, TEST_MODE>(l);
947
	step< 4, TEST_MODE>(l);
948
	step< 5, TEST_MODE>(l);
949
	step< 6, TEST_MODE>(l);
950
	step< 7, TEST_MODE>(l);
951
	step< 8, TEST_MODE>(l);
952
	step< 9, TEST_MODE>(l);
953
	step<10, TEST_MODE>(l);
954
	step<11, TEST_MODE>(l);
955
	step<12, TEST_MODE>(l);
956
	step<13, TEST_MODE>(l);
957
	step<14, TEST_MODE>(l);
958
	step<15, TEST_MODE>(l);
959
	step<16, TEST_MODE>(l);
960
	step<17, TEST_MODE>(l);
961
962
	allowed_offset = std::max<int>(0, allowed_offset - 18); // see writePort()
963
}
964
965
void YM2413::writePort(bool port, uint8_t value, int cycle_offset)
966
{
967
	// Hack: detect too-fast access and workaround that.
968
	//  Ideally we'd just pass this too-fast-access to the NukeYKY code,
969
	//  because it handles it 'fine' (as in "the same as the real
970
	//  hardware"). Possibly with a warning.
971
	//  Though currently in openMSX, when running at 'speed > 100%' we keep
972
	//  emulate the sound devices at 100% speed, but because CPU runs
973
	//  faster this can result in artificial too-fast-access. We need to
974
	//  solve this properly, but for now this hack should suffice.
975
976
	if (speedUpHack) [[unlikely]] {
977
		while (cycle_offset < allowed_offset) [[unlikely]] {
978
			float d = 0.0f;
979
			std::array<float*, 9 + 5> dummy = {
980
				&d, &d, &d, &d, &d, &d, &d, &d, &d,
981
				&d, &d, &d, &d, &d,
982
			};
983
			step18<false>(dummy); // discard result
984
		}
985
		// Need 12 cycles (@3.57MHz) wait after address-port access, 84 cycles
986
		// after data-port access. Divide by 4 to translate to our 18-step
987
		// timescale.
988
		allowed_offset = ((port ? 84 : 12) / 4) + cycle_offset;
989
	}
990
991
	writes[cycle_offset] = {.port = port, .value = value};
992
	if (port && (write_address == 0xf)) {
993
		test_mode_active = true;
994
	}
995
996
	// only needed for peekRegs()
997
	if (port == 0) {
998
		latch = value & 63;
999
	} else {
1000
		regs[latch] = value;
1001
	}
1002
}
1003
1004
void YM2413::pokeReg(uint8_t reg, uint8_t value)
1005
{
1006
	regs[reg] = value;
1007
	if (reg < 0x10) {
1008
		doModeWrite(reg, value);
1009
	} else {
1010
		if (uint8_t ch = reg & 0xf; ch < 9) {
1011
			doRegWrite(reg & 0xf0, ch, value);
1012
		}
1013
	}
1014
}
1015
1016
std::span<const uint8_t, 64> YM2413::peekRegs() const
1017
{
1018
	return regs;
1019
}
1020
1021
float YM2413::getAmplificationFactor() const
1022
{
1023
	return 1.0f / 256.0f;
1024
}
1025
1026
void YM2413::setSpeed(double speed)
1027
{
1028
	speedUpHack = speed > 1.0;
1029
}
1030
1031
} // namespace YM2413NukeYKT
1032
1033
1034
static constexpr auto egStateInfo = std::to_array<enum_string<YM2413NukeYKT::YM2413::EgState>>({
1035
	{ "attack",  YM2413NukeYKT::YM2413::EgState::attack },
1036
	{ "decay",   YM2413NukeYKT::YM2413::EgState::decay },
1037
	{ "sustain", YM2413NukeYKT::YM2413::EgState::sustain },
1038
	{ "release", YM2413NukeYKT::YM2413::EgState::release },
1039
});
1040
SERIALIZE_ENUM(YM2413NukeYKT::YM2413::EgState, egStateInfo);
1041
1042
namespace YM2413NukeYKT {
1043
1044
template<typename Archive>
1045
void YM2413::Write::serialize(Archive& ar, unsigned /*version*/)
1046
{
1047
	ar.serialize("port", port,
1048
	             "value", value);
1049
}
1050
1051
template<typename Archive>
1052
void YM2413::serialize(Archive& ar, unsigned /*version*/)
1053
{
1054
	ar.serialize("writes", writes,
1055
	             "write_data", write_data,
1056
	             "fm_data", fm_data,
1057
	             "write_address", write_address,
1058
	             "write_fm_cycle", write_fm_cycle,
1059
	             "fast_fm_rewrite", fast_fm_rewrite,
1060
	             "test_mode_active", test_mode_active);
1061
1062
	ar.serialize("eg_timer", eg_timer,
1063
	             "eg_sl", eg_sl,
1064
	             "eg_out", eg_out,
1065
	             "eg_counter_state", eg_counter_state,
1066
	             "eg_timer_shift", eg_timer_shift,
1067
	             "eg_timer_shift_lock", eg_timer_shift_lock,
1068
	             "eg_timer_lock", eg_timer_lock,
1069
	             "eg_state", eg_state,
1070
	             "eg_level", eg_level,
1071
	             "eg_rate", eg_rate,
1072
	             "eg_dokon", eg_dokon,
1073
	             "eg_kon", eg_kon,
1074
	             "eg_off", eg_off,
1075
	             "eg_timer_shift_stop", eg_timer_shift_stop,
1076
	             "pg_phase", pg_phase);
1077
1078
	ar.serialize("op_fb1", op_fb1,
1079
	             "op_fb2", op_fb2,
1080
	             "op_mod", op_mod,
1081
	             "op_phase", op_phase,
1082
	             "lfo_counter", lfo_counter,
1083
	             "lfo_am_counter", lfo_am_counter,
1084
	             "lfo_vib_counter", lfo_vib_counter,
1085
	             "lfo_am_out", lfo_am_out,
1086
	             "lfo_am_step", lfo_am_step,
1087
	             "lfo_am_dir", lfo_am_dir);
1088
1089
	ar.serialize("rm_noise", rm_noise,
1090
	             "rm_tc_bits", rm_tc_bits,
1091
	             "c_dcm", c_dcm,
1092
	             "regs", regs,
1093
	             "latch", latch);
1094
1095
	if constexpr (Archive::IS_LOADER) {
1096
		// restore redundant state
1097
		attackPtr = attack[eg_timer_shift_lock][eg_timer_lock];
1098
		auto idx = release.index[eg_timer_shift_lock][eg_timer_lock][eg_counter_state];
1099
		releasePtr = release.data[idx];
1100
1101
		lfo_vib = VIB_TAB[lfo_vib_counter];
1102
1103
		delay6 = delay7 = delay10 = delay11 = delay12 = 0;
1104
1105
		// Restore these from register values:
1106
		//   fnum, block, p_ksl, p_incr, p_ksr_freq, sk_on, vol8,
1107
		//   inst, p_inst, rhythm, testMode, patches[0]
1108
		for (auto i : xrange(uint8_t(64))) {
1109
			pokeReg(i, regs[i]);
1110
		}
1111
	}
1112
}
1113
1114
} // namespace YM2413NukeYKT
1115
1116
using YM2413NukeYKT::YM2413;
1117
INSTANTIATE_SERIALIZE_METHODS(YM2413);
1118
REGISTER_POLYMORPHIC_INITIALIZER(YM2413Core, YM2413, "YM2413-NukeYKT");
1119
1120
} // namespace openmsx