OXYGENHUB
◆ main openmsx / src / sound / AY8910.cc
28.9 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
e8555c89 Andy McCall 1
/*
2
 * Emulation of the AY-3-8910
3
 *
4
 * Original code taken from xmame-0.37b16.1
5
 *   Based on various code snippets by Ville Hallik, Michael Cuddy,
6
 *   Tatsuyuki Satoh, Fabrice Frances, Nicola Salmoria.
7
 * Integrated into openMSX by ???.
8
 * Refactored in C++ style by Maarten ter Huurne.
9
 */
10
11
#include "AY8910.hh"
12
13
#include "AY8910Periphery.hh"
14
#include "DeviceConfig.hh"
15
#include "GlobalSettings.hh"
16
#include "MSXException.hh"
17
18
#include "Math.hh"
19
#include "StringOp.hh"
20
#include "cstd.hh"
21
#include "narrow.hh"
22
#include "one_of.hh"
23
#include "outer.hh"
24
#include "random.hh"
25
#include "serialize.hh"
26
#include "xrange.hh"
27
28
#include <algorithm>
29
#include <array>
30
#include <cassert>
31
#include <iostream>
32
33
namespace openmsx {
34
35
// The step clock for the tone and noise generators is the chip clock
36
// divided by 8; for the envelope generator of the AY-3-8910, it is half
37
// that much (clock/16).
38
static constexpr float NATIVE_FREQ_FLOAT = (3579545.0f / 2) / 8;
39
static constexpr int NATIVE_FREQ_INT = int(cstd::round(NATIVE_FREQ_FLOAT));
40
41
static constexpr int PORT_A_DIRECTION = 0x40;
42
static constexpr int PORT_B_DIRECTION = 0x80;
43
44
static constexpr uint8_t AY_AFINE    =  0;
45
static constexpr uint8_t AY_ACOARSE  =  1;
46
static constexpr uint8_t AY_BFINE    =  2;
47
static constexpr uint8_t AY_BCOARSE  =  3;
48
static constexpr uint8_t AY_CFINE    =  4;
49
static constexpr uint8_t AY_CCOARSE  =  5;
50
static constexpr uint8_t AY_NOISEPER =  6;
51
static constexpr uint8_t AY_ENABLE   =  7;
52
static constexpr uint8_t AY_AVOL     =  8;
53
static constexpr uint8_t AY_BVOL     =  9;
54
static constexpr uint8_t AY_CVOL     = 10;
55
static constexpr uint8_t AY_EFINE    = 11;
56
static constexpr uint8_t AY_ECOARSE  = 12;
57
static constexpr uint8_t AY_ESHAPE   = 13;
58
static constexpr uint8_t AY_PORTA    = 14;
59
static constexpr uint8_t AY_PORTB    = 15;
60
61
// Calculate the volume->voltage conversion table. The AY-3-8910 has 16 levels,
62
// in a logarithmic scale (3dB per step). YM2149 has 32 levels, the 16 extra
63
// levels are only used for envelope volumes
64
static constexpr auto YM2149EnvelopeTab = [] {
65
	std::array<float, 32> result = {};
66
	double out = 1.0;
67
	double factor = cstd::pow<5, 3>(0.5, 0.25); // 1/sqrt(sqrt(2)) ~= 1/(1.5dB)
68
	for (int i = 31; i > 0; --i) {
69
		result[i] = float(out);
70
		out *= factor;
71
	}
72
	result[0] = 0.0f;
73
	return result;
74
}();
75
static constexpr auto AY8910EnvelopeTab = [] {
76
	// only 16 envelope steps, duplicate every step
77
	std::array<float, 32> result = {};
78
	result[0] = 0.0f;
79
	result[1] = 0.0f;
80
	for (int i = 2; i < 32; i += 2) {
81
		result[i + 0] = YM2149EnvelopeTab[i + 1];
82
		result[i + 1] = YM2149EnvelopeTab[i + 1];
83
	}
84
	return result;
85
}();
86
static constexpr auto volumeTab = [] {
87
	std::array<float, 16> result = {};
88
	result[0] = 0.0f;
89
	for (auto i : xrange(1, 16)) {
90
		result[i] = YM2149EnvelopeTab[2 * i + 1];
91
	}
92
	return result;
93
}();
94
95
96
// 256 random floats, each in range [-1, 1).
97
//  (with entries 0..2 repeated at entries 256..258).
98
static constexpr auto noiseTab = []{
99
	std::array<float, 256 + 3> result = {};
100
	// Seed doesn't matter. It's OK that these 'random' numbers are the same
101
	// in each openMSX run.
102
	PCG<0x123456789abcdefLL> pcg;
103
	for (int i = 0; i < 256; ++i) {
104
		result[i] = 2.0f * getCanonicalFloat(pcg()) - 1.0f;
105
	}
106
	result[256] = result[0];
107
	result[257] = result[1];
108
	result[258] = result[2];
109
	return result;
110
}();
111
112
static float noiseValue(float x)
113
{
114
	// cubic hermite spline interpolation
115
	assert(0.0f <= x);
116
	auto xi = int(x);
117
	auto xf = x - narrow_cast<float>(xi);
118
	xi &= 255;
119
	return Math::cubicHermite(subspan<4>(noiseTab, xi), xf);
120
}
121
122
123
// Generator:
124
125
inline void AY8910::Generator::reset()
126
{
127
	count = 0;
128
}
129
130
inline void AY8910::Generator::setPeriod(int value)
131
{
132
	// Careful studies of the chip output prove that it instead counts up from
133
	// 0 until the counter becomes greater or equal to the period. This is an
134
	// important difference when the program is rapidly changing the period to
135
	// modulate the sound.
136
	// Also, note that period = 0 is the same as period = 1. This is mentioned
137
	// in the YM2203 data sheets. However, this does NOT apply to the Envelope
138
	// period. In that case, period = 0 is half as period = 1.
139
	period = std::max(1, value);
140
	count = std::min(count, period - 1);
141
}
142
143
inline unsigned AY8910::Generator::getNextEventTime() const
144
{
145
	assert(count < period);
146
	return period - count;
147
}
148
149
inline void AY8910::Generator::advanceFast(unsigned duration)
150
{
151
	count += narrow<int>(duration);
152
	assert(count < period);
153
}
154
155
156
// ToneGenerator:
157
158
AY8910::ToneGenerator::ToneGenerator()
159
{
160
	reset();
161
}
162
163
inline void AY8910::ToneGenerator::reset()
164
{
165
	Generator::reset();
166
	output = false;
167
}
168
169
int AY8910::ToneGenerator::getDetune(const AY8910& ay8910)
170
{
171
	int result = 0;
172
	if (float vibPerc = ay8910.vibratoPercent.getFloat();
173
	    vibPerc != 0.0f) {
174
		auto vibratoPeriod = int(
175
			NATIVE_FREQ_FLOAT /
176
			ay8910.vibratoFrequency.getFloat());
177
		vibratoCount += period;
178
		vibratoCount %= vibratoPeriod;
179
		result += narrow_cast<int>(
180
			sinf((float(2 * Math::pi) * narrow_cast<float>(vibratoCount)) / narrow_cast<float>(vibratoPeriod))
181
			* vibPerc * 0.01f * narrow_cast<float>(period));
182
	}
183
	if (float detunePerc = ay8910.detunePercent.getFloat();
184
	    detunePerc != 0.0f) {
185
		float detunePeriod = NATIVE_FREQ_FLOAT /
186
			ay8910.detuneFrequency.getFloat();
187
		detuneCount += period;
188
		float noiseIdx = narrow_cast<float>(detuneCount) / detunePeriod;
189
		float detuneNoise = noiseValue(       noiseIdx)
190
		                  + noiseValue(2.0f * noiseIdx) * 0.5f;
191
		result += narrow_cast<int>(detuneNoise * detunePerc * 0.01f * narrow_cast<float>(period));
192
	}
193
	return std::min(result, period - 1);
194
}
195
196
inline void AY8910::ToneGenerator::advance(unsigned duration)
197
{
198
	assert(count < period);
199
	count += narrow<int>(duration);
200
	if (count >= period) {
201
		// Calculate number of output transitions.
202
		int cycles = count / period;
203
		count -= period * cycles; // equivalent to count %= period;
204
		output ^= cycles & 1;
205
	}
206
}
207
208
inline void AY8910::ToneGenerator::doNextEvent(const AY8910& ay8910)
209
{
210
	if (ay8910.doDetune) [[unlikely]] {
211
		count = getDetune(ay8910);
212
	} else {
213
		count = 0;
214
	}
215
	output ^= 1;
216
}
217
218
219
// NoiseGenerator:
220
221
AY8910::NoiseGenerator::NoiseGenerator()
222
{
223
	reset();
224
}
225
226
inline void AY8910::NoiseGenerator::reset()
227
{
228
	Generator::reset();
229
	random = 1;
230
}
231
232
inline void AY8910::NoiseGenerator::doNextEvent()
233
{
234
	count = 0;
235
236
	// The Random Number Generator of the 8910 is a 17-bit shift register.
237
	// The input to the shift register is bit0 XOR bit3 (bit0 is the
238
	// output). Verified on real AY8910 and YM2149 chips.
239
	//
240
	// Fibonacci configuration:
241
	//   random ^= ((random & 1) ^ ((random >> 3) & 1)) << 17;
242
	//   random >>= 1;
243
	// Galois configuration:
244
	//   if (random & 1) random ^= 0x24000;
245
	//   random >>= 1;
246
	// or alternatively:
247
	random = (random >> 1) ^ ((random & 1) << 13) ^ ((random & 1) << 16);
248
}
249
250
inline void AY8910::NoiseGenerator::advance(unsigned duration)
251
{
252
	assert(count < period);
253
	count += narrow<int>(duration);
254
	int cycles = count / period;
255
	count -= cycles * period; // equivalent to count %= period
256
257
	// The following loops advance the random state N steps at once. The
258
	// values for N (4585, 275, 68, 8, 1) are chosen so that:
259
	// - The formulas are relatively simple.
260
	// - The ratio between the step sizes is roughly the same.
261
	for (/**/; cycles >= 4585; cycles -= 4585) {
262
		random = ((random & 0x1f) << 12)
263
		       ^ ((random & 0x1f) <<  9)
264
		       ^   random
265
		       ^ ( random         >>  5);
266
	}
267
	for (/**/; cycles >= 275; cycles -= 275) {
268
		random = ((random & 0x03f) << 11)
269
		       ^ ((random & 0x1c0) <<  8)
270
		       ^ ((random & 0x1ff) <<  5)
271
		       ^   random
272
		       ^ ( random          >>  6)
273
		       ^ ( random          >>  9);
274
	}
275
	for (/**/; cycles >= 68; cycles -= 68) {
276
		random = ((random & 0xfff) <<  5)
277
		       ^ ((random & 0xfff) <<  2)
278
		       ^   random
279
		       ^ ( random          >> 12);
280
	}
281
	for (/**/; cycles >= 8; cycles -= 8) {
282
		random = ((random & 0xff) << 9)
283
		       ^ ((random & 0xff) << 6)
284
		       ^ ( random         >> 8);
285
	}
286
	for (/**/; cycles >= 1; cycles -= 1) {
287
		random = ((random & 1) << 16)
288
		       ^ ((random & 1) << 13)
289
		       ^ ( random      >>  1);
290
	}
291
}
292
293
294
// Amplitude:
295
296
static bool checkAY8910(const DeviceConfig& config)
297
{
298
	auto type = config.getChildData("type", "ay8910");
299
	StringOp::casecmp cmp;
300
	if (cmp(type, "ay8910")) {
301
		return true;
302
	} else if (cmp(type, "ym2149")) {
303
		return false;
304
	}
305
	throw FatalError("Unknown PSG type: ", type);
306
}
307
308
AY8910::Amplitude::Amplitude(const DeviceConfig& config)
309
	: isAY8910(checkAY8910(config))
310
	, envVolTable(isAY8910 ? AY8910EnvelopeTab : YM2149EnvelopeTab)
311
{
312
	vol[0] = vol[1] = vol[2] = 0.0f;
313
	envChan[0] = false;
314
	envChan[1] = false;
315
	envChan[2] = false;
316
317
	if (false) {
318
		std::cout << "YM2149Envelope:";
319
		for (const auto& e : YM2149EnvelopeTab) {
320
			std::cout << ' ' << std::hexfloat << e;
321
		}
322
		std::cout << "\nAY8910Envelope:";
323
		for (const auto& e : AY8910EnvelopeTab) {
324
			std::cout << ' ' << std::hexfloat << e;
325
		}
326
		std::cout << "\nvolume:";
327
		for (const auto& e : volumeTab) {
328
			std::cout << ' ' << std::hexfloat << e;
329
		}
330
		std::cout << '\n';
331
	}
332
}
333
334
inline float AY8910::Amplitude::getVolume(unsigned chan) const
335
{
336
	assert(!followsEnvelope(chan));
337
	return vol[chan];
338
}
339
340
inline void AY8910::Amplitude::setChannelVolume(unsigned chan, unsigned value)
341
{
342
	envChan[chan] = (value & 0x10) != 0;
343
	vol[chan] = volumeTab[value & 0x0F];
344
}
345
346
inline bool AY8910::Amplitude::followsEnvelope(unsigned chan) const
347
{
348
	return envChan[chan];
349
}
350
351
352
// Envelope:
353
354
// AY8910 and YM2149 behave different here:
355
//  YM2149 envelope goes twice as fast and has twice as many levels. Here
356
//  we implement the YM2149 behaviour, but to get the AY8910 behaviour we
357
//  repeat every level twice in the envVolTable
358
359
inline AY8910::Envelope::Envelope(std::span<const float, 32> envVolTable_)
360
	: envVolTable(envVolTable_)
361
{
362
}
363
364
inline void AY8910::Envelope::reset()
365
{
366
	count = 0;
367
}
368
369
inline void AY8910::Envelope::setPeriod(int value)
370
{
371
	// twice as fast as AY8910
372
	//  see also Generator::setPeriod()
373
	period = std::max(1, 2 * value);
374
	count = std::min(count, period - 1);
375
}
376
377
inline float AY8910::Envelope::getVolume() const
378
{
379
	return envVolTable[step ^ attack];
380
}
381
382
inline void AY8910::Envelope::setShape(unsigned shape)
383
{
384
	// do 32 steps for both AY8910 and YM2149
385
	/*
386
	envelope shapes:
387
		C AtAlH
388
		0 0 x x  \___
389
		0 1 x x  /___
390
		1 0 0 0  \\\\
391
		1 0 0 1  \___
392
		1 0 1 0  \/\/
393
		1 0 1 1  \
394
		1 1 0 0  ////
395
		1 1 0 1  /
396
		1 1 1 0  /\/\
397
		1 1 1 1  /___
398
	*/
399
	attack = (shape & 0x04) ? 0x1F : 0x00;
400
	if ((shape & 0x08) == 0) {
401
		// If Continue = 0, map the shape to the equivalent one
402
		// which has Continue = 1.
403
		hold = true;
404
		alternate = attack != 0;
405
	} else {
406
		hold = (shape & 0x01) != 0;
407
		alternate = (shape & 0x02) != 0;
408
	}
409
	count = 0;
410
	step = 0x1F;
411
	holding = false;
412
}
413
414
inline bool AY8910::Envelope::isChanging() const
415
{
416
	return !holding;
417
}
418
419
inline void AY8910::Envelope::doSteps(int steps)
420
{
421
	// For best performance callers should check upfront whether
422
	//    isChanging() == true
423
	// Though we can't assert on it because the condition might change
424
	// in the inner loop(s) of generateChannels().
425
	//assert(!holding);
426
427
	if (holding) return;
428
	step -= steps;
429
430
	// Check current envelope position.
431
	if (step < 0) {
432
		if (hold) {
433
			if (alternate) attack ^= 0x1F;
434
			holding = true;
435
			step = 0;
436
		} else {
437
			// If step has looped an odd number of times
438
			// (usually 1), invert the output.
439
			if (alternate && (step & 0x10)) {
440
				attack ^= 0x1F;
441
			}
442
			step &= 0x1F;
443
		}
444
	}
445
}
446
447
inline void AY8910::Envelope::advance(unsigned duration)
448
{
449
	assert(count < period);
450
	count += narrow<int>(duration * 2);
451
	if (count >= period) {
452
		int steps = count / period;
453
		count -= steps * period; // equivalent to count %= period;
454
		doSteps(steps);
455
	}
456
}
457
458
inline void AY8910::Envelope::doNextEvent()
459
{
460
	count = 0;
461
	doSteps(period == 1 ? 2 : 1);
462
}
463
464
inline unsigned AY8910::Envelope::getNextEventTime() const
465
{
466
	assert(count < period);
467
	return (period - count + 1) / 2;
468
}
469
470
inline void AY8910::Envelope::advanceFast(unsigned duration)
471
{
472
	count += narrow<int>(2 * duration);
473
	assert(count < period);
474
}
475
476
477
478
// AY8910 main class:
479
480
AY8910::AY8910(const std::string& name_, AY8910Periphery& periphery_,
481
               const DeviceConfig& config, EmuTime time)
482
	: ResampledSoundDevice(config.getMotherBoard(), name_, "PSG", 3, NATIVE_FREQ_INT, false)
483
	, periphery(periphery_)
484
	, debuggable(config.getMotherBoard(), getName())
485
	, vibratoPercent(
486
		config.getCommandController(), tmpStrCat(getName(), "_vibrato_percent"),
487
		"controls strength of vibrato effect", 0.0, 0.0, 10.0)
488
	, vibratoFrequency(
489
		config.getCommandController(), tmpStrCat(getName(), "_vibrato_frequency"),
490
		"frequency of vibrato effect in Hertz", 5, 1.0, 10.0)
491
	, detunePercent(
492
		config.getCommandController(), tmpStrCat(getName(), "_detune_percent"),
493
		"controls strength of detune effect", 0.0, 0.0, 10.0)
494
	, detuneFrequency(
495
		config.getCommandController(), tmpStrCat(getName(), "_detune_frequency"),
496
		"frequency of detune effect in Hertz", 5.0, 1.0, 100.0)
497
	, directionsCallback(
498
		config.getGlobalSettings().getInvalidPsgDirectionsSetting())
499
	, amplitude(config)
500
	, envelope(amplitude.getEnvVolTable())
501
	, isAY8910(checkAY8910(config))
502
	, ignorePortDirections(config.getChildDataAsBool("ignorePortDirections", true))
503
{
504
	update(vibratoPercent);
505
506
	// make valgrind happy
507
	std::ranges::fill(regs, 0);
508
509
	reset(time);
510
	registerSound(config);
511
512
	// only attach once all initialization is successful
513
	vibratoPercent.attach(*this);
514
	detunePercent .attach(*this);
515
}
516
517
AY8910::~AY8910()
518
{
519
	vibratoPercent.detach(*this);
520
	detunePercent .detach(*this);
521
522
	unregisterSound();
523
}
524
525
void AY8910::reset(EmuTime time)
526
{
527
	// Reset generators and envelope.
528
	for (auto& t : tone) t.reset();
529
	noise.reset();
530
	envelope.reset();
531
	// Reset registers and values derived from them.
532
	for (auto reg : xrange(16)) {
533
		wrtReg(reg, 0, time);
534
	}
535
}
536
537
538
uint8_t AY8910::readRegister(unsigned reg, EmuTime time)
539
{
540
	if (reg >= 16) return 255;
541
	switch (reg) {
542
	case AY_PORTA:
543
		if (!(regs[AY_ENABLE] & PORT_A_DIRECTION)) { // input
544
			regs[reg] = periphery.readA(time);
545
		}
546
		break;
547
	case AY_PORTB:
548
		if (!(regs[AY_ENABLE] & PORT_B_DIRECTION)) { // input
549
			regs[reg] = periphery.readB(time);
550
		}
551
		break;
552
	}
553
554
	// TODO some AY8910 models have 1F as mask for registers 1, 3, 5
555
	static constexpr std::array<uint8_t, 16> regMask = {
556
		0xff, 0x0f, 0xff, 0x0f, 0xff, 0x0f, 0x1f, 0xff,
557
		0x1f, 0x1f ,0x1f, 0xff, 0xff, 0x0f, 0xff, 0xff
558
	};
559
	return isAY8910 ? regs[reg] & regMask[reg]
560
	                : regs[reg];
561
}
562
563
uint8_t AY8910::peekRegister(unsigned reg, EmuTime time) const
564
{
565
	if (reg >= 16) return 255;
566
	switch (reg) {
567
	case AY_PORTA:
568
		if (!(regs[AY_ENABLE] & PORT_A_DIRECTION)) { // input
569
			return periphery.readA(time);
570
		}
571
		break;
572
	case AY_PORTB:
573
		if (!(regs[AY_ENABLE] & PORT_B_DIRECTION)) { // input
574
			return periphery.readB(time);
575
		}
576
		break;
577
	}
578
	return regs[reg];
579
}
580
581
582
void AY8910::writeRegister(unsigned reg, uint8_t value, EmuTime time)
583
{
584
	if (reg >= 16) return;
585
	if ((reg < AY_PORTA) && (reg == AY_ESHAPE || regs[reg] != value)) {
586
		// Update the output buffer before changing the register.
587
		updateStream(time);
588
	}
589
	wrtReg(reg, value, time);
590
}
591
void AY8910::wrtReg(unsigned reg, uint8_t value, EmuTime time)
592
{
593
	// Warn/force port directions
594
	if (reg == AY_ENABLE) {
595
		if (value & PORT_A_DIRECTION) {
596
			directionsCallback.execute();
597
		}
598
		if (ignorePortDirections)
599
		{
600
			// portA -> input
601
			// portB -> output
602
			value = (value & ~PORT_A_DIRECTION) | PORT_B_DIRECTION;
603
		}
604
	}
605
606
	// Note: unused bits are stored as well; they can be read back.
607
	uint8_t diff = regs[reg] ^ value;
608
	regs[reg] = value;
609
610
	switch (reg) {
611
	case AY_AFINE:
612
	case AY_ACOARSE:
613
	case AY_BFINE:
614
	case AY_BCOARSE:
615
	case AY_CFINE:
616
	case AY_CCOARSE:
617
		tone[reg / 2].setPeriod(regs[reg & ~1] + 256 * (regs[reg | 1] & 0x0F));
618
		break;
619
	case AY_NOISEPER:
620
		// Half the frequency of tone generation.
621
		//
622
		// Verified on turboR GT: value=0 and value=1 sound the same.
623
		//
624
		// Likely in real AY8910 this is implemented by driving the
625
		// noise generator at halve the frequency instead of
626
		// multiplying the value by 2 (hence the correction for value=0
627
		// here). But the effect is the same(?).
628
		noise.setPeriod(2 * std::max(1, value & 0x1F));
629
		break;
630
	case AY_AVOL:
631
	case AY_BVOL:
632
	case AY_CVOL:
633
		amplitude.setChannelVolume(reg - AY_AVOL, value);
634
		break;
635
	case AY_EFINE:
636
	case AY_ECOARSE:
637
		// also half the frequency of tone generation, but handled
638
		// inside Envelope::setPeriod()
639
		envelope.setPeriod(regs[AY_EFINE] + 256 * regs[AY_ECOARSE]);
640
		break;
641
	case AY_ESHAPE:
642
		envelope.setShape(value);
643
		break;
644
	case AY_ENABLE:
645
		if (diff & PORT_A_DIRECTION) {
646
			// port A changed
647
			if (value & PORT_A_DIRECTION) {
648
				// from input to output
649
				periphery.writeA(regs[AY_PORTA], time);
650
			} else {
651
				// from output to input
652
				periphery.writeA(0xff, time);
653
			}
654
		}
655
		if (diff & PORT_B_DIRECTION) {
656
			// port B changed
657
			if (value & PORT_B_DIRECTION) {
658
				// from input to output
659
				periphery.writeB(regs[AY_PORTB], time);
660
			} else {
661
				// from output to input
662
				periphery.writeB(0xff, time);
663
			}
664
		}
665
		break;
666
	case AY_PORTA:
667
		if (regs[AY_ENABLE] & PORT_A_DIRECTION) { // output
668
			periphery.writeA(value, time);
669
		}
670
		break;
671
	case AY_PORTB:
672
		if (regs[AY_ENABLE] & PORT_B_DIRECTION) { // output
673
			periphery.writeB(value, time);
674
		}
675
		break;
676
	}
677
}
678
679
[[nodiscard]] static inline float calc(bool b, float f)
680
{
681
	// Calculates:
682
	//   return b ? f : 0.0f;
683
	// Though this generates branchless code, might be faster
684
	return narrow<float>(b) * f;
685
}
686
[[nodiscard]] static inline float calc(bool b1, bool b2, float f)
687
{
688
	return narrow<float>(b1 && b2) * f;
689
}
690
691
void AY8910::generateChannels(std::span<float*> bufs, unsigned num)
692
{
693
	// Disable channels with volume 0: since the sample value doesn't matter,
694
	// we can use the fastest path.
695
	unsigned chanEnable = regs[AY_ENABLE];
696
	for (auto chan : xrange(3)) {
697
		if ((!amplitude.followsEnvelope(chan) &&
698
		     (amplitude.getVolume(chan) == 0.0f)) ||
699
		    (amplitude.followsEnvelope(chan) &&
700
		     !envelope.isChanging() &&
701
		     (envelope.getVolume() == 0.0f))) {
702
			bufs[chan] = nullptr;
703
			tone[chan].advance(num);
704
			chanEnable |= 0x09 << chan;
705
		}
706
	}
707
	// Noise disabled on all channels?
708
	if ((chanEnable & 0x38) == 0x38) {
709
		noise.advance(num);
710
	}
711
712
	// Calculate samples.
713
	// The 8910 has three outputs, each output is the mix of one of the
714
	// three tone generators and of the (single) noise generator. The two
715
	// are mixed BEFORE going into the DAC. The formula to mix each channel
716
	// is:
717
	//   (ToneOn | ToneDisable) & (NoiseOn | NoiseDisable),
718
	//   where ToneOn and NoiseOn are the current generator state
719
	//   and ToneDisable and NoiseDisable come from the enable reg.
720
	// Note that this means that if both tone and noise are disabled, the
721
	// output is 1, not 0, and can be modulated by changing the volume.
722
	bool envelopeUpdated = false;
723
	Envelope initialEnvelope = envelope;
724
	NoiseGenerator initialNoise = noise;
725
	for (unsigned chan = 0; chan < 3; ++chan, chanEnable >>= 1) {
726
		auto* buf = bufs[chan];
727
		if (!buf) continue;
728
		ToneGenerator& t = tone[chan];
729
		if (envelope.isChanging() && amplitude.followsEnvelope(chan)) {
730
			envelopeUpdated = true;
731
			envelope = initialEnvelope;
732
			if ((chanEnable & 0x09) == 0x08) {
733
				// no noise, square wave: alternating between 0 and 1.
734
				auto val = calc(t.getOutput(), envelope.getVolume());
735
				unsigned remaining = num;
736
				unsigned nextE = envelope.getNextEventTime();
737
				unsigned nextT = t.getNextEventTime();
738
				while ((nextT <= remaining) || (nextE <= remaining)) {
739
					if (nextT < nextE) {
740
						addFill(buf, val, nextT);
741
						remaining -= nextT;
742
						nextE -= nextT;
743
						envelope.advanceFast(nextT);
744
						t.doNextEvent(*this);
745
						nextT = t.getNextEventTime();
746
					} else if (nextE < nextT) {
747
						addFill(buf, val, nextE);
748
						remaining -= nextE;
749
						nextT -= nextE;
750
						t.advanceFast(nextE);
751
						envelope.doNextEvent();
752
						nextE = envelope.getNextEventTime();
753
					} else {
754
						assert(nextT == nextE);
755
						addFill(buf, val, nextT);
756
						remaining -= nextT;
757
						t.doNextEvent(*this);
758
						nextT = t.getNextEventTime();
759
						envelope.doNextEvent();
760
						nextE = envelope.getNextEventTime();
761
					}
762
					val = calc(t.getOutput(), envelope.getVolume());
763
				}
764
				if (remaining) {
765
					// last interval (without events)
766
					addFill(buf, val, remaining);
767
					t.advanceFast(remaining);
768
					envelope.advanceFast(remaining);
769
				}
770
771
			} else if ((chanEnable & 0x09) == 0x09) {
772
				// no noise, channel disabled: always 1.
773
				auto val = envelope.getVolume();
774
				unsigned remaining = num;
775
				unsigned next = envelope.getNextEventTime();
776
				while (next <= remaining) {
777
					addFill(buf, val, next);
778
					remaining -= next;
779
					envelope.doNextEvent();
780
					val = envelope.getVolume();
781
					next = envelope.getNextEventTime();
782
				}
783
				if (remaining) {
784
					// last interval (without events)
785
					addFill(buf, val, remaining);
786
					envelope.advanceFast(remaining);
787
				}
788
				t.advance(num);
789
790
			} else if ((chanEnable & 0x09) == 0x00) {
791
				// noise enabled, tone enabled
792
				noise = initialNoise;
793
				auto val = calc(noise.getOutput(), t.getOutput(), envelope.getVolume());
794
				unsigned remaining = num;
795
				unsigned nextT = t.getNextEventTime();
796
				unsigned nextN = noise.getNextEventTime();
797
				unsigned nextE = envelope.getNextEventTime();
798
				unsigned next = std::min({nextT, nextN, nextE});
799
				while (next <= remaining) {
800
					addFill(buf, val, next);
801
					remaining -= next;
802
					nextT -= next;
803
					nextN -= next;
804
					nextE -= next;
805
					if (nextT) {
806
						t.advanceFast(next);
807
					} else {
808
						t.doNextEvent(*this);
809
						nextT = t.getNextEventTime();
810
					}
811
					if (nextN) {
812
						noise.advanceFast(next);
813
					} else {
814
						noise.doNextEvent();
815
						nextN = noise.getNextEventTime();
816
					}
817
					if (nextE) {
818
						envelope.advanceFast(next);
819
					} else {
820
						envelope.doNextEvent();
821
						nextE = envelope.getNextEventTime();
822
					}
823
					next = std::min({nextT, nextN, nextE});
824
					val = calc(noise.getOutput(), t.getOutput(), envelope.getVolume());
825
				}
826
				if (remaining) {
827
					// last interval (without events)
828
					addFill(buf, val, remaining);
829
					t.advanceFast(remaining);
830
					noise.advanceFast(remaining);
831
					envelope.advanceFast(remaining);
832
				}
833
834
			} else {
835
				// noise enabled, tone disabled
836
				noise = initialNoise;
837
				auto val = calc(noise.getOutput(), envelope.getVolume());
838
				unsigned remaining = num;
839
				unsigned nextE = envelope.getNextEventTime();
840
				unsigned nextN = noise.getNextEventTime();
841
				while ((nextN <= remaining) || (nextE <= remaining)) {
842
					if (nextN < nextE) {
843
						addFill(buf, val, nextN);
844
						remaining -= nextN;
845
						nextE -= nextN;
846
						envelope.advanceFast(nextN);
847
						noise.doNextEvent();
848
						nextN = noise.getNextEventTime();
849
					} else if (nextE < nextN) {
850
						addFill(buf, val, nextE);
851
						remaining -= nextE;
852
						nextN -= nextE;
853
						noise.advanceFast(nextE);
854
						envelope.doNextEvent();
855
						nextE = envelope.getNextEventTime();
856
					} else {
857
						assert(nextN == nextE);
858
						addFill(buf, val, nextN);
859
						remaining -= nextN;
860
						noise.doNextEvent();
861
						nextN = noise.getNextEventTime();
862
						envelope.doNextEvent();
863
						nextE = envelope.getNextEventTime();
864
					}
865
					val = calc(noise.getOutput(), envelope.getVolume());
866
				}
867
				if (remaining) {
868
					// last interval (without events)
869
					addFill(buf, val, remaining);
870
					noise.advanceFast(remaining);
871
					envelope.advanceFast(remaining);
872
				}
873
				t.advance(num);
874
			}
875
		} else {
876
			// no (changing) envelope on this channel
877
			auto volume = amplitude.followsEnvelope(chan)
878
			            ? envelope.getVolume()
879
			            : amplitude.getVolume(chan);
880
			if ((chanEnable & 0x09) == 0x08) {
881
				// no noise, square wave: alternating between 0 and 1.
882
				auto val = calc(t.getOutput(), volume);
883
				unsigned remaining = num;
884
				unsigned next = t.getNextEventTime();
885
				while (next <= remaining) {
886
					addFill(buf, val, next);
887
					val = volume - val;
888
					remaining -= next;
889
					t.doNextEvent(*this);
890
					next = t.getNextEventTime();
891
				}
892
				if (remaining) {
893
					// last interval (without events)
894
					addFill(buf, val, remaining);
895
					t.advanceFast(remaining);
896
				}
897
898
			} else if ((chanEnable & 0x09) == 0x09) {
899
				// no noise, channel disabled: always 1.
900
				addFill(buf, volume, num);
901
				t.advance(num);
902
903
			} else if ((chanEnable & 0x09) == 0x00) {
904
				// noise enabled, tone enabled
905
				noise = initialNoise;
906
				auto val1 = calc(t.getOutput(), volume);
907
				auto val2 = calc(noise.getOutput(), val1);
908
				unsigned remaining = num;
909
				unsigned nextN = noise.getNextEventTime();
910
				unsigned nextT = t.getNextEventTime();
911
				while ((nextN <= remaining) || (nextT <= remaining)) {
912
					if (nextT < nextN) {
913
						addFill(buf, val2, nextT);
914
						remaining -= nextT;
915
						nextN -= nextT;
916
						noise.advanceFast(nextT);
917
						t.doNextEvent(*this);
918
						nextT = t.getNextEventTime();
919
						val1 = volume - val1;
920
						val2 = calc(noise.getOutput(), val1);
921
					} else if (nextN < nextT) {
922
						addFill(buf, val2, nextN);
923
						remaining -= nextN;
924
						nextT -= nextN;
925
						t.advanceFast(nextN);
926
						noise.doNextEvent();
927
						nextN = noise.getNextEventTime();
928
						val2 = calc(noise.getOutput(), val1);
929
					} else {
930
						assert(nextT == nextN);
931
						addFill(buf, val2, nextT);
932
						remaining -= nextT;
933
						t.doNextEvent(*this);
934
						nextT = t.getNextEventTime();
935
						noise.doNextEvent();
936
						nextN = noise.getNextEventTime();
937
						val1 = volume - val1;
938
						val2 = calc(noise.getOutput(), val1);
939
					}
940
				}
941
				if (remaining) {
942
					// last interval (without events)
943
					addFill(buf, val2, remaining);
944
					t.advanceFast(remaining);
945
					noise.advanceFast(remaining);
946
				}
947
948
			} else {
949
				// noise enabled, tone disabled
950
				noise = initialNoise;
951
				unsigned remaining = num;
952
				auto val = calc(noise.getOutput(), volume);
953
				unsigned next = noise.getNextEventTime();
954
				while (next <= remaining) {
955
					addFill(buf, val, next);
956
					remaining -= next;
957
					noise.doNextEvent();
958
					val = calc(noise.getOutput(), volume);
959
					next = noise.getNextEventTime();
960
				}
961
				if (remaining) {
962
					// last interval (without events)
963
					addFill(buf, val, remaining);
964
					noise.advanceFast(remaining);
965
				}
966
				t.advance(num);
967
			}
968
		}
969
	}
970
971
	// Envelope not yet updated?
972
	if (envelope.isChanging() && !envelopeUpdated) {
973
		envelope.advance(num);
974
	}
975
}
976
977
float AY8910::getAmplificationFactorImpl() const
978
{
979
	return 1.0f;
980
}
981
982
void AY8910::update(const Setting& setting) noexcept
983
{
984
	if (&setting == one_of(&vibratoPercent, &detunePercent)) {
985
		doDetune = (vibratoPercent.getFloat() != 0.0f) ||
986
			   (detunePercent .getFloat() != 0.0f);
987
	} else {
988
		ResampledSoundDevice::update(setting);
989
	}
990
}
991
992
993
// Debuggable
994
995
AY8910::Debuggable::Debuggable(MSXMotherBoard& motherBoard_, const std::string& name_)
996
	: SimpleDebuggable(motherBoard_, name_ + " regs", "PSG", 0x10)
997
{
998
}
999
1000
uint8_t AY8910::Debuggable::read(unsigned address, EmuTime time)
1001
{
1002
	auto& ay8910 = OUTER(AY8910, debuggable);
1003
	return ay8910.readRegister(address, time);
1004
}
1005
1006
void AY8910::Debuggable::write(unsigned address, uint8_t value, EmuTime time)
1007
{
1008
	auto& ay8910 = OUTER(AY8910, debuggable);
1009
	ay8910.writeRegister(address, value, time);
1010
}
1011
1012
1013
template<typename Archive>
1014
void AY8910::serialize(Archive& ar, unsigned /*version*/)
1015
{
1016
	ar.serialize("toneGenerators", tone,
1017
	             "noiseGenerator", noise,
1018
	             "envelope",       envelope,
1019
	             "registers",      regs);
1020
1021
	// amplitude
1022
	if constexpr (Archive::IS_LOADER) {
1023
		for (auto i : xrange(3)) {
1024
			amplitude.setChannelVolume(i, regs[i + AY_AVOL]);
1025
		}
1026
	}
1027
}
1028
INSTANTIATE_SERIALIZE_METHODS(AY8910);
1029
1030
// version 1: initial version
1031
// version 2: removed 'output' member variable
1032
template<typename Archive>
1033
void AY8910::Generator::serialize(Archive& ar, unsigned /*version*/)
1034
{
1035
	ar.serialize("period", period,
1036
	             "count", count);
1037
}
1038
INSTANTIATE_SERIALIZE_METHODS(AY8910::Generator);
1039
1040
// version 1: initial version
1041
// version 2: moved 'output' variable from base class to here
1042
template<typename Archive>
1043
void AY8910::ToneGenerator::serialize(Archive& ar, unsigned version)
1044
{
1045
	ar.template serializeInlinedBase<Generator>(*this, version);
1046
	ar.serialize("vibratoCount", vibratoCount,
1047
	             "detuneCount", detuneCount);
1048
	if (ar.versionAtLeast(version, 2)) {
1049
		ar.serialize("output", output);
1050
	} else {
1051
		// don't bother trying to restore this from the old location:
1052
		// it doesn't influence any MSX-observable state, and the
1053
		// difference in generated sound will likely be inaudible
1054
	}
1055
}
1056
INSTANTIATE_SERIALIZE_METHODS(AY8910::ToneGenerator);
1057
1058
// version 1: initial version
1059
// version 2: removed 'output' variable from base class, not stored here but
1060
//            instead it's calculated from 'random' when needed
1061
template<typename Archive>
1062
void AY8910::NoiseGenerator::serialize(Archive& ar, unsigned version)
1063
{
1064
	ar.template serializeInlinedBase<Generator>(*this, version);
1065
	ar.serialize("random", random);
1066
}
1067
INSTANTIATE_SERIALIZE_METHODS(AY8910::NoiseGenerator);
1068
1069
template<typename Archive>
1070
void AY8910::Envelope::serialize(Archive& ar, unsigned /*version*/)
1071
{
1072
	ar.serialize("period",    period,
1073
	             "count",     count,
1074
	             "step",      step,
1075
	             "attack",    attack,
1076
	             "hold",      hold,
1077
	             "alternate", alternate,
1078
	             "holding",   holding);
1079
}
1080
INSTANTIATE_SERIALIZE_METHODS(AY8910::Envelope);
1081
1082
} // namespace openmsx