openMSX
Y8950.cc
Go to the documentation of this file.
1/*
2 * Based on:
3 * emu8950.c -- Y8950 emulator written by Mitsutaka Okazaki 2001
4 * heavily rewritten to fit openMSX structure
5 */
6
7#include "Y8950.hh"
8#include "Y8950Periphery.hh"
9#include "MSXAudio.hh"
10#include "DeviceConfig.hh"
11#include "MSXMotherBoard.hh"
12#include "Math.hh"
13#include "cstd.hh"
14#include "enumerate.hh"
15#include "narrow.hh"
16#include "outer.hh"
17#include "ranges.hh"
18#include "serialize.hh"
19#include "xrange.hh"
20#include <algorithm>
21#include <array>
22#include <cmath>
23#include <iostream>
24
25namespace openmsx {
26
27static constexpr unsigned EG_MUTE = 1 << Y8950::EG_BITS;
28static constexpr Y8950::EnvPhaseIndex EG_DP_MAX = Y8950::EnvPhaseIndex(EG_MUTE);
29
30static constexpr unsigned MOD = 0;
31static constexpr unsigned CAR = 1;
32
33static constexpr double EG_STEP = 0.1875; // 3/16
34static constexpr double SL_STEP = 3.0;
35static constexpr double TL_STEP = 0.75; // 12/16
36static constexpr double DB_STEP = 0.1875; // 3/16
37
38static constexpr unsigned SL_PER_EG = 16; // SL_STEP / EG_STEP
39static constexpr unsigned TL_PER_EG = 4; // TL_STEP / EG_STEP
40static constexpr unsigned EG_PER_DB = 1; // EG_STEP / DB_STEP
41
42// PM speed(Hz) and depth(cent)
43static constexpr double PM_SPEED = 6.4;
44static constexpr double PM_DEPTH = 13.75 / 2;
45static constexpr double PM_DEPTH2 = 13.75;
46
47// Dynamic range of sustain level
48static constexpr int SL_BITS = 4;
49static constexpr int SL_MUTE = 1 << SL_BITS;
50// Size of sin table ( 1 -- 18 can be used, but 7 -- 14 recommended.)
51static constexpr int PG_BITS = 10;
52static constexpr int PG_WIDTH = 1 << PG_BITS;
53static constexpr int PG_MASK = PG_WIDTH - 1;
54// Phase increment counter
55static constexpr int DP_BITS = 19;
56static constexpr int DP_BASE_BITS = DP_BITS - PG_BITS;
57
58// Dynamic range
59static constexpr int DB_BITS = 9;
60static constexpr int DB_MUTE = 1 << DB_BITS;
61// PM table is calculated by PM_AMP * exp2(PM_DEPTH * sin(x) / 1200)
62static constexpr int PM_AMP_BITS = 8;
63static constexpr int PM_AMP = 1 << PM_AMP_BITS;
64
65// Bits for liner value
66static constexpr int DB2LIN_AMP_BITS = 11;
67static constexpr int SLOT_AMP_BITS = DB2LIN_AMP_BITS;
68
69// Bits for Pitch and Amp modulator
70static constexpr int PM_PG_BITS = 8;
71static constexpr int PM_PG_WIDTH = 1 << PM_PG_BITS;
72static constexpr int PM_DP_BITS = 16;
73static constexpr int PM_DP_WIDTH = 1 << PM_DP_BITS;
74static constexpr int AM_PG_BITS = 8;
75static constexpr int AM_PG_WIDTH = 1 << AM_PG_BITS;
76static constexpr int AM_DP_BITS = 16;
77static constexpr int AM_DP_WIDTH = 1 << AM_DP_BITS;
78
79// LFO Table
80static constexpr unsigned PM_DPHASE = unsigned(PM_SPEED * PM_DP_WIDTH / (Y8950::CLOCK_FREQ / double(Y8950::CLOCK_FREQ_DIV)));
81
82
83// LFO Amplitude Modulation table (verified on real YM3812)
84// 27 output levels (triangle waveform);
85// 1 level takes one of: 192, 256 or 448 samples
86//
87// Length: 210 elements.
88// Each of the elements has to be repeated
89// exactly 64 times (on 64 consecutive samples).
90// The whole table takes: 64 * 210 = 13440 samples.
91//
92// Verified on real YM3812 (OPL2), but I believe it's the same for Y8950
93// because it closely matches the Y8950 AM parameters:
94// speed = 3.7Hz
95// depth = 4.875dB
96// Also this approach can be easily implemented in HW, the previous one (see SVN
97// history) could not.
98static constexpr unsigned LFO_AM_TAB_ELEMENTS = 210;
99static constexpr std::array<int8_t, LFO_AM_TAB_ELEMENTS> lfo_am_table =
100{
101 0,0,0,0,0,0,0,
102 1,1,1,1,
103 2,2,2,2,
104 3,3,3,3,
105 4,4,4,4,
106 5,5,5,5,
107 6,6,6,6,
108 7,7,7,7,
109 8,8,8,8,
110 9,9,9,9,
111 10,10,10,10,
112 11,11,11,11,
113 12,12,12,12,
114 13,13,13,13,
115 14,14,14,14,
116 15,15,15,15,
117 16,16,16,16,
118 17,17,17,17,
119 18,18,18,18,
120 19,19,19,19,
121 20,20,20,20,
122 21,21,21,21,
123 22,22,22,22,
124 23,23,23,23,
125 24,24,24,24,
126 25,25,25,25,
127 26,26,26,
128 25,25,25,25,
129 24,24,24,24,
130 23,23,23,23,
131 22,22,22,22,
132 21,21,21,21,
133 20,20,20,20,
134 19,19,19,19,
135 18,18,18,18,
136 17,17,17,17,
137 16,16,16,16,
138 15,15,15,15,
139 14,14,14,14,
140 13,13,13,13,
141 12,12,12,12,
142 11,11,11,11,
143 10,10,10,10,
144 9,9,9,9,
145 8,8,8,8,
146 7,7,7,7,
147 6,6,6,6,
148 5,5,5,5,
149 4,4,4,4,
150 3,3,3,3,
151 2,2,2,2,
152 1,1,1,1
153};
154
155//**************************************************//
156// //
157// Helper functions //
158// //
159//**************************************************//
160
161static constexpr unsigned DB_POS(int x)
162{
163 auto result = int(x / DB_STEP);
164 assert(result < DB_MUTE);
165 assert(result >= 0);
166 return result;
167}
168static constexpr unsigned DB_NEG(int x)
169{
170 return 2 * DB_MUTE + DB_POS(x);
171}
172
173//**************************************************//
174// //
175// Create tables //
176// //
177//**************************************************//
178
179// Linear to Log curve conversion table (for Attack rate) and vice versa.
180// values are in the range [0 .. EG_MUTE]
181// adjustAR[] and adjustRA[] are each others inverse, IOW
182// adjustRA[adjustAR[x]] == x
183// (except for rounding errors).
184static constexpr auto adjustAR = [] {
185 std::array<unsigned, EG_MUTE> result = {};
186 result[0] = EG_MUTE;
187 auto log_eg_mute = cstd::log<6, 5>(EG_MUTE);
188 for (int i : xrange(1, int(EG_MUTE))) {
189 result[i] = narrow_cast<unsigned>((EG_MUTE - 1 - EG_MUTE * cstd::log<6, 5>(i) / log_eg_mute) / 2);
190 assert(0 <= int(result[i]));
191 assert(result[i] <= EG_MUTE);
192 }
193 return result;
194}();
195static constexpr auto adjustRA = [] {
196 std::array<unsigned, EG_MUTE + 1> result = {};
197 result[0] = EG_MUTE;
198 for (int i : xrange(1, int(EG_MUTE))) {
199 result[i] = narrow_cast<unsigned>(cstd::pow<6, 5>(EG_MUTE, (double(EG_MUTE) - 1 - 2 * i) / EG_MUTE));
200 assert(0 <= int(result[i]));
201 assert(result[i] <= EG_MUTE);
202 }
203 result[EG_MUTE] = 0;
204 return result;
205}();
206
207// Table for dB(0 -- (1<<DB_BITS)) to Liner(0 -- DB2LIN_AMP_WIDTH)
208static constexpr auto dB2LinTab = [] {
209 std::array<int, (2 * DB_MUTE) * 2> result = {};
210 for (int i : xrange(DB_MUTE)) {
211 result[i] = int(double((1 << DB2LIN_AMP_BITS) - 1) *
212 cstd::pow<7, 3>(10, -double(i) * DB_STEP / 20.0));
213 }
214 assert(result[DB_MUTE - 1] == 0);
215 for (auto i : xrange(DB_MUTE, 2 * DB_MUTE)) {
216 result[i] = 0;
217 }
218 for (auto i : xrange(2 * DB_MUTE)) {
219 result[i + 2 * DB_MUTE] = -result[i];
220 }
221 return result;
222}();
223
224// WaveTable for each envelope amp.
225// values are in range[ 0, DB_MUTE) (for positive values)
226// or [2*DB_MUTE, 3*DB_MUTE) (for negative values)
227static constexpr auto sinTable = [] {
228 // Linear(+0.0 ... +1.0) to dB(DB_MUTE-1 ... 0)
229 auto lin2db = [](double d) {
230 if (d < 1e-4) { // (almost) zero
231 return DB_MUTE - 1;
232 }
233 int tmp = -int(20.0 * cstd::log10<6, 2>(d) / DB_STEP);
234 int result = std::min(tmp, DB_MUTE - 1);
235 assert(result >= 0);
236 assert(result <= DB_MUTE - 1);
237 return result;
238 };
239
240 std::array<unsigned, PG_WIDTH> result = {};
241 for (int i : xrange(PG_WIDTH / 4)) {
242 result[i] = lin2db(cstd::sin<2>(2.0 * Math::pi * i / PG_WIDTH));
243 }
244 for (auto i : xrange(PG_WIDTH / 4)) {
245 result[PG_WIDTH / 2 - 1 - i] = result[i];
246 }
247 for (auto i : xrange(PG_WIDTH / 2)) {
248 result[PG_WIDTH / 2 + i] = 2 * DB_MUTE + result[i];
249 }
250 return result;
251}();
252
253// Table for Pitch Modulator
254static constexpr auto pmTable = [] {
255 std::array<std::array<int, PM_PG_WIDTH>, 2> result = {};
256 for (int i : xrange(PM_PG_WIDTH)) {
257 auto s = cstd::sin<5>(2.0 * Math::pi * i / PM_PG_WIDTH) / 1200;
258 result[0][i] = int(PM_AMP * cstd::exp2<2>(PM_DEPTH * s));
259 result[1][i] = int(PM_AMP * cstd::exp2<2>(PM_DEPTH2 * s));
260 }
261 return result;
262}();
263
264// TL Table.
265static constexpr auto tllTable = [] {
266 // Processed version of Table 3.5 from the Application Manual
267 constexpr std::array<int, 16> klTable = {
268 0, 24, 32, 37, 40, 43, 45, 47, 48, 50, 51, 52, 53, 54, 55, 56
269 };
270 // This is indeed {0.0, 3.0, 1.5, 6.0} dB/oct, verified on real Y8950.
271 // Note the illogical order of 2nd and 3rd element.
272 constexpr std::array<int, 4> shift = { 31, 1, 2, 0 };
273
274 std::array<std::array<int, 4>, 16 * 8> result = {};
275 for (auto freq : xrange(16 * 8)) {
276 int fnum = freq % 16;
277 int block = freq / 16;
278 int tmp = 4 * klTable[fnum] - 32 * (7 - block);
279 for (auto KL : xrange(4)) {
280 result[freq][KL] = (tmp <= 0) ? 0 : (tmp >> shift[KL]);
281 }
282 }
283 return result;
284}();
285
286// Phase incr table for Attack.
287static constexpr auto dPhaseArTable = [] {
288 std::array<std::array<Y8950::EnvPhaseIndex, 16>, 16> result = {};
289 for (auto Rks : xrange(16)) {
290 result[Rks][0] = Y8950::EnvPhaseIndex(0);
291 for (auto AR : xrange(1, 15)) {
292 int RM = std::min(AR + (Rks >> 2), 15);
293 int RL = Rks & 3;
294 result[Rks][AR] =
295 Y8950::EnvPhaseIndex(12 * (RL + 4)) >> (15 - RM);
296 }
297 result[Rks][15] = EG_DP_MAX;
298 }
299 return result;
300}();
301
302// Phase incr table for Decay and Release.
303static constexpr auto dPhaseDrTable = [] {
304 std::array<std::array<Y8950::EnvPhaseIndex, 16>, 16> result = {};
305 for (auto Rks : xrange(16)) {
306 result[Rks][0] = Y8950::EnvPhaseIndex(0);
307 for (auto DR : xrange(1, 16)) {
308 int RM = std::min(DR + (Rks >> 2), 15);
309 int RL = Rks & 3;
310 result[Rks][DR] =
311 Y8950::EnvPhaseIndex(RL + 4) >> (15 - RM);
312 }
313 }
314 return result;
315}();
316
317
318// class Y8950::Patch
319
320Y8950::Patch::Patch()
321{
322 reset();
323}
324
325void Y8950::Patch::reset()
326{
327 AM = false;
328 PM = false;
329 EG = false;
330 ML = 0;
331 KL = 0;
332 TL = 0;
333 AR = 0;
334 DR = 0;
335 SL = 0;
336 RR = 0;
337 setKeyScaleRate(false);
338 setFeedbackShift(0);
339}
340
341
342// class Y8950::Slot
343
344Y8950::Slot::Slot()
345 : dPhaseARTableRks(dPhaseArTable[0])
346 , dPhaseDRTableRks(dPhaseDrTable[0])
347{
348}
349
350void Y8950::Slot::reset()
351{
352 phase = 0;
353 output = 0;
354 feedback = 0;
355 eg_mode = EnvelopeState::FINISH;
356 eg_phase = EG_DP_MAX;
357 key = 0;
358 patch.reset();
359
360 // this initializes:
361 // dPhase, tll, dPhaseARTableRks, dPhaseDRTableRks, eg_dPhase
362 updateAll(0);
363}
364
365void Y8950::Slot::updatePG(unsigned freq)
366{
367 static constexpr std::array<int, 16> mlTable = {
368 1, 1*2, 2*2, 3*2, 4*2, 5*2, 6*2 , 7*2,
369 8*2, 9*2, 10*2, 10*2, 12*2, 12*2, 15*2, 15*2
370 };
371
372 unsigned fnum = freq % 1024;
373 unsigned block = freq / 1024;
374 dPhase = ((fnum * mlTable[patch.ML]) << block) >> (21 - DP_BITS);
375}
376
377void Y8950::Slot::updateTLL(unsigned freq)
378{
379 tll = tllTable[freq >> 6][patch.KL] + narrow<int>(patch.TL * TL_PER_EG);
380}
381
382void Y8950::Slot::updateRKS(unsigned freq)
383{
384 unsigned rks = freq >> patch.KR;
385 assert(rks < 16);
386 dPhaseARTableRks = dPhaseArTable[rks];
387 dPhaseDRTableRks = dPhaseDrTable[rks];
388}
389
390void Y8950::Slot::updateEG()
391{
392 switch (eg_mode) {
393 using enum EnvelopeState;
394 case ATTACK:
395 eg_dPhase = dPhaseARTableRks[patch.AR];
396 break;
397 case DECAY:
398 eg_dPhase = dPhaseDRTableRks[patch.DR];
399 break;
400 case SUSTAIN:
401 case RELEASE:
402 eg_dPhase = dPhaseDRTableRks[patch.RR];
403 break;
404 case FINISH:
405 eg_dPhase = Y8950::EnvPhaseIndex(0);
406 break;
407 }
408}
409
410void Y8950::Slot::updateAll(unsigned freq)
411{
412 updatePG(freq);
413 updateTLL(freq);
414 updateRKS(freq);
415 updateEG(); // EG should be last
416}
417
418bool Y8950::Slot::isActive() const
419{
420 return eg_mode != EnvelopeState::FINISH;
421}
422
423// Slot key on
424void Y8950::Slot::slotOn(KeyPart part)
425{
426 if (!key) {
427 eg_mode = EnvelopeState::ATTACK;
428 phase = 0;
429 eg_phase = Y8950::EnvPhaseIndex(adjustRA[eg_phase.toInt()]);
430 }
431 key |= part;
432}
433
434// Slot key off
435void Y8950::Slot::slotOff(KeyPart part)
436{
437 if (key) {
438 key &= ~part;
439 if (!key) {
440 if (eg_mode == EnvelopeState::ATTACK) {
441 eg_phase = Y8950::EnvPhaseIndex(adjustAR[eg_phase.toInt()]);
442 }
443 eg_mode = EnvelopeState::RELEASE;
444 }
445 }
446}
447
448
449// class Y8950::Channel
450
451Y8950::Channel::Channel()
452{
453 reset();
454}
455
456void Y8950::Channel::reset()
457{
458 setFreq(0);
459 slot[MOD].reset();
460 slot[CAR].reset();
461 alg = false;
462}
463
464// Set frequency (combined F-Number (10bit) and Block (3bit))
465void Y8950::Channel::setFreq(unsigned freq_)
466{
467 freq = freq_;
468}
469
470void Y8950::Channel::keyOn(KeyPart part)
471{
472 slot[MOD].slotOn(part);
473 slot[CAR].slotOn(part);
474}
475
476void Y8950::Channel::keyOff(KeyPart part)
477{
478 slot[MOD].slotOff(part);
479 slot[CAR].slotOff(part);
480}
481
482static constexpr auto INPUT_RATE = unsigned(cstd::round(Y8950::CLOCK_FREQ / double(Y8950::CLOCK_FREQ_DIV)));
483
484Y8950::Y8950(const std::string& name_, const DeviceConfig& config,
485 unsigned sampleRam, EmuTime::param time, MSXAudio& audio)
486 : ResampledSoundDevice(config.getMotherBoard(), name_, "MSX-AUDIO", 9 + 5 + 1, INPUT_RATE, false)
487 , motherBoard(config.getMotherBoard())
488 , periphery(audio.createPeriphery(getName()))
489 , adpcm(*this, config, name_, sampleRam)
490 , connector(motherBoard.getPluggingController())
491 , dac13(name_ + " DAC", "MSX-AUDIO 13-bit DAC", config)
492 , debuggable(motherBoard, getName())
493 , timer1(EmuTimer::createOPL3_1(motherBoard.getScheduler(), *this))
494 , timer2(EmuTimer::createOPL3_2(motherBoard.getScheduler(), *this))
495 , irq(motherBoard, getName() + ".IRQ")
496{
497 // For debugging: print out tables to be able to compare before/after
498 // when the calculation changes.
499 if (false) {
500 for (auto i : xrange(PM_PG_WIDTH)) {
501 std::cout << pmTable[0][i] << ' '
502 << pmTable[1][i] << '\n';
503 }
504 std::cout << '\n';
505
506 for (auto i : xrange(EG_MUTE)) {
507 std::cout << adjustRA[i] << ' '
508 << adjustAR[i] << '\n';
509 }
510 std::cout << adjustRA[EG_MUTE] << "\n\n";
511
512 for (const auto& e : dB2LinTab) std::cout << e << '\n';
513 std::cout << '\n';
514
515 for (auto i : xrange(16 * 8)) {
516 for (auto j : xrange(4)) {
517 std::cout << tllTable[i][j] << ' ';
518 }
519 std::cout << '\n';
520 }
521 std::cout << '\n';
522
523 for (const auto& e : sinTable) std::cout << e << '\n';
524 std::cout << '\n';
525
526 for (auto i : xrange(16)) {
527 for (auto j : xrange(16)) {
528 std::cout << dPhaseArTable[i][j].getRawValue() << ' ';
529 }
530 std::cout << '\n';
531 }
532 std::cout << '\n';
533
534 for (auto i : xrange(16)) {
535 for (auto j : xrange(16)) {
536 std::cout << dPhaseDrTable[i][j].getRawValue() << ' ';
537 }
538 std::cout << '\n';
539 }
540 std::cout << '\n';
541 }
542
543 reset(time);
544 registerSound(config);
545}
546
551
553{
554 adpcm.clearRam();
555}
556
557// Reset whole of opl except patch data.
558void Y8950::reset(EmuTime::param time)
559{
560 for (auto& c : ch) c.reset();
561
562 rythm_mode = false;
563 am_mode = false;
564 pm_mode = false;
565 pm_phase = 0;
566 am_phase = 0;
567 noise_seed = 0xffff;
568 noiseA_phase = 0;
569 noiseB_phase = 0;
570 noiseA_dPhase = 0;
571 noiseB_dPhase = 0;
572
573 // update the output buffer before changing the register
574 updateStream(time);
575 ranges::fill(reg, 0x00);
576
577 reg[0x04] = 0x18;
578 reg[0x19] = 0x0F; // fixes 'Thunderbirds are Go'
579 status = 0x00;
580 statusMask = 0;
581 irq.reset();
582
583 adpcm.reset(time);
584}
585
586
587// Drum key on
588void Y8950::keyOn_BD() { ch[6].keyOn(KEY_RHYTHM); }
589void Y8950::keyOn_HH() { ch[7].slot[MOD].slotOn(KEY_RHYTHM); }
590void Y8950::keyOn_SD() { ch[7].slot[CAR].slotOn(KEY_RHYTHM); }
591void Y8950::keyOn_TOM() { ch[8].slot[MOD].slotOn(KEY_RHYTHM); }
592void Y8950::keyOn_CYM() { ch[8].slot[CAR].slotOn(KEY_RHYTHM); }
593
594// Drum key off
595void Y8950::keyOff_BD() { ch[6].keyOff(KEY_RHYTHM); }
596void Y8950::keyOff_HH() { ch[7].slot[MOD].slotOff(KEY_RHYTHM); }
597void Y8950::keyOff_SD() { ch[7].slot[CAR].slotOff(KEY_RHYTHM); }
598void Y8950::keyOff_TOM(){ ch[8].slot[MOD].slotOff(KEY_RHYTHM); }
599void Y8950::keyOff_CYM(){ ch[8].slot[CAR].slotOff(KEY_RHYTHM); }
600
601// Change Rhythm Mode
602void Y8950::setRythmMode(int data)
603{
604 bool newMode = (data & 32) != 0;
605 if (rythm_mode != newMode) {
606 rythm_mode = newMode;
607 if (!rythm_mode) {
608 // ON->OFF
609 keyOff_BD(); // TODO keyOff() or immediately to FINISH?
610 keyOff_HH(); // other variants use keyOff(), but
611 keyOff_SD(); // verify on real HW
612 keyOff_TOM();
613 keyOff_CYM();
614 }
615 }
616}
617
618// recalculate 'key' from register settings
619void Y8950::update_key_status()
620{
621 for (auto [i, c] : enumerate(ch)) {
622 uint8_t main = (reg[0xb0 + i] & 0x20) ? KEY_MAIN : 0;
623 c.slot[MOD].key = main;
624 c.slot[CAR].key = main;
625 }
626 if (rythm_mode) {
627 ch[6].slot[MOD].key |= uint8_t((reg[0xbd] & 0x10) ? KEY_RHYTHM : 0); // BD1
628 ch[6].slot[CAR].key |= uint8_t((reg[0xbd] & 0x10) ? KEY_RHYTHM : 0); // BD2
629 ch[7].slot[MOD].key |= uint8_t((reg[0xbd] & 0x01) ? KEY_RHYTHM : 0); // HH
630 ch[7].slot[CAR].key |= uint8_t((reg[0xbd] & 0x08) ? KEY_RHYTHM : 0); // SD
631 ch[8].slot[MOD].key |= uint8_t((reg[0xbd] & 0x04) ? KEY_RHYTHM : 0); // TOM
632 ch[8].slot[CAR].key |= uint8_t((reg[0xbd] & 0x02) ? KEY_RHYTHM : 0); // CYM
633 }
634}
635
636
637//
638// Generate wave data
639//
640
641// Convert Amp(0 to EG_HEIGHT) to Phase(0 to 8PI).
642static constexpr int wave2_8pi(int e)
643{
644 int shift = SLOT_AMP_BITS - PG_BITS - 2;
645 return (shift > 0) ? (e >> shift) : (e << -shift);
646}
647
648unsigned Y8950::Slot::calc_phase(int lfo_pm)
649{
650 if (patch.PM) {
651 phase += (dPhase * lfo_pm) >> PM_AMP_BITS;
652 } else {
653 phase += dPhase;
654 }
655 return phase >> DP_BASE_BITS;
656}
657
658static constexpr auto S2E(int x) {
659 return Y8950::EnvPhaseIndex(int(x / EG_STEP));
660}
661static constexpr std::array<Y8950::EnvPhaseIndex, 16> SL = {
662 S2E( 0), S2E( 3), S2E( 6), S2E( 9), S2E(12), S2E(15), S2E(18), S2E(21),
663 S2E(24), S2E(27), S2E(30), S2E(33), S2E(36), S2E(39), S2E(42), S2E(93)
664};
665unsigned Y8950::Slot::calc_envelope(int lfo_am)
666{
667 unsigned egOut = 0;
668 switch (eg_mode) {
669 using enum EnvelopeState;
670 case ATTACK:
671 eg_phase += eg_dPhase;
672 if (eg_phase >= EG_DP_MAX) {
673 egOut = 0;
674 eg_phase = Y8950::EnvPhaseIndex(0);
675 eg_mode = DECAY;
676 updateEG();
677 } else {
678 egOut = adjustAR[eg_phase.toInt()];
679 }
680 break;
681
682 case DECAY:
683 eg_phase += eg_dPhase;
684 if (eg_phase >= SL[patch.SL]) {
685 eg_phase = SL[patch.SL];
686 eg_mode = SUSTAIN;
687 updateEG();
688 }
689 egOut = eg_phase.toInt();
690 break;
691
692 case SUSTAIN:
693 if (!patch.EG) {
694 eg_phase += eg_dPhase;
695 }
696 egOut = eg_phase.toInt();
697 if (egOut >= EG_MUTE) {
698 eg_phase = EG_DP_MAX;
699 eg_mode = FINISH;
700 egOut = EG_MUTE - 1;
701 }
702 break;
703
704 case RELEASE:
705 eg_phase += eg_dPhase;
706 egOut = eg_phase.toInt();
707 if (egOut >= EG_MUTE) {
708 eg_phase = EG_DP_MAX;
709 eg_mode = FINISH;
710 egOut = EG_MUTE - 1;
711 }
712 break;
713
714 case FINISH:
715 egOut = EG_MUTE - 1;
716 break;
717 }
718
719 egOut = ((egOut + tll) * EG_PER_DB);
720 if (patch.AM) {
721 egOut += lfo_am;
722 }
723 return std::min<unsigned>(egOut, DB_MUTE - 1);
724}
725
726int Y8950::Slot::calc_slot_car(int lfo_pm, int lfo_am, int fm)
727{
728 unsigned egOut = calc_envelope(lfo_am);
729 int pgout = narrow<int>(calc_phase(lfo_pm)) + wave2_8pi(fm);
730 return dB2LinTab[sinTable[pgout & PG_MASK] + egOut];
731}
732
733int Y8950::Slot::calc_slot_mod(int lfo_pm, int lfo_am)
734{
735 unsigned egOut = calc_envelope(lfo_am);
736 unsigned pgout = calc_phase(lfo_pm);
737
738 if (patch.FB != 0) {
739 pgout += wave2_8pi(feedback) >> patch.FB;
740 }
741 int newOutput = dB2LinTab[sinTable[pgout & PG_MASK] + egOut];
742 feedback = (output + newOutput) >> 1;
743 output = newOutput;
744 return feedback;
745}
746
747int Y8950::Slot::calc_slot_tom(int lfo_pm, int lfo_am)
748{
749 unsigned egOut = calc_envelope(lfo_am);
750 unsigned pgout = calc_phase(lfo_pm);
751 return dB2LinTab[sinTable[pgout & PG_MASK] + egOut];
752}
753
754int Y8950::Slot::calc_slot_snare(int lfo_pm, int lfo_am, int whiteNoise)
755{
756 unsigned egOut = calc_envelope(lfo_am);
757 unsigned pgout = calc_phase(lfo_pm);
758 unsigned tmp = (pgout & (1 << (PG_BITS - 1))) ? 0 : 2 * DB_MUTE;
759 return (dB2LinTab[tmp + egOut] + dB2LinTab[egOut + whiteNoise]) >> 1;
760}
761
762int Y8950::Slot::calc_slot_cym(int lfo_am, int a, int b)
763{
764 unsigned egOut = calc_envelope(lfo_am);
765 return (dB2LinTab[egOut + a] + dB2LinTab[egOut + b]) >> 1;
766}
767
768// HI-HAT
769int Y8950::Slot::calc_slot_hat(int lfo_am, int a, int b, int whiteNoise)
770{
771 unsigned egOut = calc_envelope(lfo_am);
772 return (dB2LinTab[egOut + whiteNoise] +
773 dB2LinTab[egOut + a] +
774 dB2LinTab[egOut + b]) >> 2;
775}
776
777float Y8950::getAmplificationFactorImpl() const
778{
779 return 1.0f / (1 << DB2LIN_AMP_BITS);
780}
781
782void Y8950::setEnabled(bool enabled_, EmuTime::param time)
783{
784 updateStream(time);
785 enabled = enabled_;
786}
787
788bool Y8950::checkMuteHelper()
789{
790 if (!enabled) {
791 return true;
792 }
793 for (auto i : xrange(6)) {
794 if (ch[i].slot[CAR].isActive()) return false;
795 }
796 if (!rythm_mode) {
797 for (auto i : xrange(6, 9)) {
798 if (ch[i].slot[CAR].isActive()) return false;
799 }
800 } else {
801 if (ch[6].slot[CAR].isActive()) return false;
802 if (ch[7].slot[MOD].isActive()) return false;
803 if (ch[7].slot[CAR].isActive()) return false;
804 if (ch[8].slot[MOD].isActive()) return false;
805 if (ch[8].slot[CAR].isActive()) return false;
806 }
807
808 return adpcm.isMuted();
809}
810
811void Y8950::generateChannels(std::span<float*> bufs, unsigned num)
812{
813 // TODO implement per-channel mute (instead of all-or-nothing)
814 if (checkMuteHelper()) {
815 // TODO update internal state even when muted
816 // during mute pm_phase, am_phase, noiseA_phase, noiseB_phase
817 // and noise_seed aren't updated, probably ok
818 ranges::fill(bufs, nullptr);
819 return;
820 }
821
822 for (auto sample : xrange(num)) {
823 // Amplitude modulation: 27 output levels (triangle waveform);
824 // 1 level takes one of: 192, 256 or 448 samples
825 // One entry from LFO_AM_TABLE lasts for 64 samples
826 // lfo_am_table is 210 elements long
827 ++am_phase;
828 if (am_phase == (LFO_AM_TAB_ELEMENTS * 64)) am_phase = 0;
829 int tmp = narrow_cast<int>(lfo_am_table[am_phase / 64]);
830 int lfo_am = am_mode ? tmp : tmp / 4;
831
832 pm_phase = (pm_phase + PM_DPHASE) & (PM_DP_WIDTH - 1);
833 int lfo_pm = pmTable[pm_mode][pm_phase >> (PM_DP_BITS - PM_PG_BITS)];
834
835 if (noise_seed & 1) {
836 noise_seed ^= 0x24000;
837 }
838 noise_seed >>= 1;
839 int whiteNoise = noise_seed & 1 ? DB_POS(6) : DB_NEG(6);
840
841 noiseA_phase += noiseA_dPhase;
842 noiseA_phase &= (0x40 << 11) - 1;
843 if ((noiseA_phase >> 11) == 0x3f) {
844 noiseA_phase = 0;
845 }
846 int noiseA = noiseA_phase & (0x03 << 11) ? DB_POS(6) : DB_NEG(6);
847
848 noiseB_phase += noiseB_dPhase;
849 noiseB_phase &= (0x10 << 11) - 1;
850 int noiseB = noiseB_phase & (0x0A << 11) ? DB_POS(6) : DB_NEG(6);
851
852 for (auto i : xrange(rythm_mode ? 6 : 9)) {
853 if (ch[i].slot[CAR].isActive()) {
854 bufs[i][sample] += narrow_cast<float>(ch[i].alg
855 ? ch[i].slot[CAR].calc_slot_car(lfo_pm, lfo_am, 0) +
856 ch[i].slot[MOD].calc_slot_mod(lfo_pm, lfo_am)
857 : ch[i].slot[CAR].calc_slot_car(lfo_pm, lfo_am,
858 ch[i].slot[MOD].calc_slot_mod(lfo_pm, lfo_am)));
859 } else {
860 //bufs[i][sample] += 0;
861 }
862 }
863 if (rythm_mode) {
864 //bufs[6][sample] += 0;
865 //bufs[7][sample] += 0;
866 //bufs[8][sample] += 0;
867
868 // TODO wasn't in original source either
869 (void)ch[7].slot[MOD].calc_phase(lfo_pm);
870 (void)ch[8].slot[CAR].calc_phase(lfo_pm);
871
872 bufs[ 9][sample] += (ch[6].slot[CAR].isActive())
873 ? narrow_cast<float>(
874 2 * ch[6].slot[CAR].calc_slot_car(lfo_pm, lfo_am,
875 ch[6].slot[MOD].calc_slot_mod(lfo_pm, lfo_am)))
876 : 0.0f;
877 bufs[10][sample] += (ch[7].slot[CAR].isActive())
878 ? narrow_cast<float>(2 * ch[7].slot[CAR].calc_slot_snare(lfo_pm, lfo_am, whiteNoise))
879 : 0.0f;
880 bufs[11][sample] += (ch[8].slot[CAR].isActive())
881 ? narrow_cast<float>(2 * ch[8].slot[CAR].calc_slot_cym(lfo_am, noiseA, noiseB))
882 : 0.0f;
883 bufs[12][sample] += (ch[7].slot[MOD].isActive())
884 ? narrow_cast<float>(2 * ch[7].slot[MOD].calc_slot_hat(lfo_am, noiseA, noiseB, whiteNoise))
885 : 0.0f;
886 bufs[13][sample] += (ch[8].slot[MOD].isActive())
887 ? narrow_cast<float>(2 * ch[8].slot[MOD].calc_slot_tom(lfo_pm, lfo_am))
888 : 0.0f;
889 } else {
890 //bufs[ 9] += 0;
891 //bufs[10] += 0;
892 //bufs[11] += 0;
893 //bufs[12] += 0;
894 //bufs[13] += 0;
895 }
896
897 bufs[14][sample] += narrow_cast<float>(adpcm.calcSample());
898 }
899}
900
901//
902// I/O Ctrl
903//
904
905void Y8950::writeReg(uint8_t rg, uint8_t data, EmuTime::param time)
906{
907 static constexpr std::array<int, 32> sTbl = {
908 0, 2, 4, 1, 3, 5, -1, -1,
909 6, 8, 10, 7, 9, 11, -1, -1,
910 12, 14, 16, 13, 15, 17, -1, -1,
911 -1, -1, -1, -1, -1, -1, -1, -1
912 };
913
914 // TODO only for registers that influence sound
915 // TODO also ADPCM
916 //if (rg >= 0x20) {
917 // update the output buffer before changing the register
918 updateStream(time);
919 //}
920
921 switch (rg & 0xe0) {
922 case 0x00: {
923 switch (rg) {
924 case 0x01: // TEST
925 // TODO
926 // Y8950 MSX-AUDIO Test register $01 (write only)
927 //
928 // Bit Description
929 //
930 // 7 Reset LFOs - seems to force the LFOs to their initial
931 // values (eg. maximum amplitude, zero phase deviation)
932 //
933 // 6 something to do with ADPCM - bit 0 of the status
934 // register is affected by setting this bit (PCM BSY)
935 //
936 // 5 No effect? - Waveform select enable in YM3812 OPL2 so seems
937 // reasonable that this bit wouldn't have been used in OPL
938 //
939 // 4 No effect?
940 //
941 // 3 Faster LFOs - increases the frequencies of the LFOs and
942 // (maybe) the timers (cf. YM2151 test register)
943 //
944 // 2 Reset phase generators - No phase generator output, but
945 // envelope generators still work (can hear a transient
946 // when they are gated)
947 //
948 // 1 No effect?
949 //
950 // 0 Reset envelopes - Envelope generator outputs forced
951 // to maximum, so all enabled voices sound at maximum
952 reg[rg] = data;
953 break;
954
955 case 0x02: // TIMER1 (resolution 80us)
956 timer1->setValue(data);
957 reg[rg] = data;
958 break;
959
960 case 0x03: // TIMER2 (resolution 320us)
961 timer2->setValue(data);
962 reg[rg] = data;
963 break;
964
965 case 0x04: // FLAG CONTROL
966 if (data & Y8950::R04_IRQ_RESET) {
967 resetStatus(0x78); // reset all flags
968 } else {
969 changeStatusMask((~data) & 0x78);
970 timer1->setStart((data & Y8950::R04_ST1) != 0, time);
971 timer2->setStart((data & Y8950::R04_ST2) != 0, time);
972 reg[rg] = data;
973 }
974 adpcm.resetStatus();
975 break;
976
977 case 0x06: // (KEYBOARD OUT)
978 connector.write(data, time);
979 reg[rg] = data;
980 break;
981
982 case 0x07: // START/REC/MEM DATA/REPEAT/SP-OFF/-/-/RESET
983 periphery.setSPOFF((data & 8) != 0, time); // bit 3
984 [[fallthrough]];
985
986 case 0x08: // CSM/KEY BOARD SPLIT/-/-/SAMPLE/DA AD/64K/ROM
987 case 0x09: // START ADDRESS (L)
988 case 0x0A: // START ADDRESS (H)
989 case 0x0B: // STOP ADDRESS (L)
990 case 0x0C: // STOP ADDRESS (H)
991 case 0x0D: // PRESCALE (L)
992 case 0x0E: // PRESCALE (H)
993 case 0x0F: // ADPCM-DATA
994 case 0x10: // DELTA-N (L)
995 case 0x11: // DELTA-N (H)
996 case 0x12: // ENVELOP CONTROL
997 case 0x1A: // PCM-DATA
998 reg[rg] = data;
999 adpcm.writeReg(rg, data, time);
1000 break;
1001
1002 case 0x15: // DAC-DATA (bit9-2)
1003 reg[rg] = data;
1004 if (reg[0x08] & 0x04) {
1005 int tmp = static_cast<signed char>(reg[0x15]) * 256
1006 + reg[0x16];
1007 tmp = (tmp * 4) >> (7 - reg[0x17]);
1008 dac13.writeDAC(Math::clipToInt16(tmp), time);
1009 }
1010 break;
1011 case 0x16: // (bit1-0)
1012 reg[rg] = data & 0xC0;
1013 break;
1014 case 0x17: // (exponent)
1015 reg[rg] = data & 0x07;
1016 break;
1017
1018 case 0x18: // I/O-CONTROL (bit3-0)
1019 // 0 -> input
1020 // 1 -> output
1021 reg[rg] = data;
1022 periphery.write(reg[0x18], reg[0x19], time);
1023 break;
1024
1025 case 0x19: // I/O-DATA (bit3-0)
1026 reg[rg] = data;
1027 periphery.write(reg[0x18], reg[0x19], time);
1028 break;
1029 }
1030 break;
1031 }
1032 case 0x20: {
1033 if (int s = sTbl[rg & 0x1f]; s >= 0) {
1034 auto& chan = ch[s / 2];
1035 auto& slot = chan.slot[s & 1];
1036 slot.patch.AM = (data >> 7) & 1;
1037 slot.patch.PM = (data >> 6) & 1;
1038 slot.patch.EG = (data >> 5) & 1;
1039 slot.patch.setKeyScaleRate((data & 0x10) != 0);
1040 slot.patch.ML = (data >> 0) & 15;
1041 slot.updateAll(chan.freq);
1042 }
1043 reg[rg] = data;
1044 break;
1045 }
1046 case 0x40: {
1047 if (int s = sTbl[rg & 0x1f]; s >= 0) {
1048 auto& chan = ch[s / 2];
1049 auto& slot = chan.slot[s & 1];
1050 slot.patch.KL = (data >> 6) & 3;
1051 slot.patch.TL = (data >> 0) & 63;
1052 slot.updateAll(chan.freq);
1053 }
1054 reg[rg] = data;
1055 break;
1056 }
1057 case 0x60: {
1058 if (int s = sTbl[rg & 0x1f]; s >= 0) {
1059 auto& slot = ch[s / 2].slot[s & 1];
1060 slot.patch.AR = (data >> 4) & 15;
1061 slot.patch.DR = (data >> 0) & 15;
1062 slot.updateEG();
1063 }
1064 reg[rg] = data;
1065 break;
1066 }
1067 case 0x80: {
1068 if (int s = sTbl[rg & 0x1f]; s >= 0) {
1069 auto& slot = ch[s / 2].slot[s & 1];
1070 slot.patch.SL = (data >> 4) & 15;
1071 slot.patch.RR = (data >> 0) & 15;
1072 slot.updateEG();
1073 }
1074 reg[rg] = data;
1075 break;
1076 }
1077 case 0xa0: {
1078 if (rg == 0xbd) {
1079 am_mode = (data & 0x80) != 0;
1080 pm_mode = (data & 0x40) != 0;
1081
1082 setRythmMode(data);
1083 if (rythm_mode) {
1084 if (data & 0x10) keyOn_BD(); else keyOff_BD();
1085 if (data & 0x08) keyOn_SD(); else keyOff_SD();
1086 if (data & 0x04) keyOn_TOM(); else keyOff_TOM();
1087 if (data & 0x02) keyOn_CYM(); else keyOff_CYM();
1088 if (data & 0x01) keyOn_HH(); else keyOff_HH();
1089 }
1090 ch[6].slot[MOD].updateAll(ch[6].freq);
1091 ch[6].slot[CAR].updateAll(ch[6].freq);
1092 ch[7].slot[MOD].updateAll(ch[7].freq);
1093 ch[7].slot[CAR].updateAll(ch[7].freq);
1094 ch[8].slot[MOD].updateAll(ch[8].freq);
1095 ch[8].slot[CAR].updateAll(ch[8].freq);
1096
1097 reg[rg] = data;
1098 break;
1099 }
1100 unsigned c = rg & 0x0f;
1101 if (c > 8) {
1102 // 0xa9-0xaf 0xb9-0xbf
1103 break;
1104 }
1105 unsigned freq = [&] {
1106 if (!(rg & 0x10)) {
1107 // 0xa0-0xa8
1108 return data | ((reg[rg + 0x10] & 0x1F) << 8);
1109 } else {
1110 // 0xb0-0xb8
1111 if (data & 0x20) {
1112 ch[c].keyOn (KEY_MAIN);
1113 } else {
1114 ch[c].keyOff(KEY_MAIN);
1115 }
1116 return reg[rg - 0x10] | ((data & 0x1F) << 8);
1117 }
1118 }();
1119 ch[c].setFreq(freq);
1120 unsigned fNum = freq % 1024;
1121 unsigned block = freq / 1024;
1122 switch (c) {
1123 case 7: noiseA_dPhase = fNum << block;
1124 break;
1125 case 8: noiseB_dPhase = fNum << block;
1126 break;
1127 }
1128 ch[c].slot[CAR].updateAll(freq);
1129 ch[c].slot[MOD].updateAll(freq);
1130 reg[rg] = data;
1131 break;
1132 }
1133 case 0xc0: {
1134 if (rg > 0xc8)
1135 break;
1136 int c = rg - 0xC0;
1137 ch[c].slot[MOD].patch.setFeedbackShift((data >> 1) & 7);
1138 ch[c].alg = data & 1;
1139 reg[rg] = data;
1140 }
1141 }
1142}
1143
1144uint8_t Y8950::readReg(uint8_t rg, EmuTime::param time)
1145{
1146 updateStream(time); // TODO only when necessary
1147
1148 switch (rg) {
1149 case 0x0F: // ADPCM-DATA
1150 case 0x13: // ???
1151 case 0x14: // ???
1152 case 0x1A: // PCM-DATA
1153 return adpcm.readReg(rg, time);
1154 default:
1155 return peekReg(rg, time);
1156 }
1157}
1158
1159uint8_t Y8950::peekReg(uint8_t rg, EmuTime::param time) const
1160{
1161 switch (rg) {
1162 case 0x05: // (KEYBOARD IN)
1163 return connector.peek(time);
1164
1165 case 0x0F: // ADPCM-DATA
1166 case 0x13: // ???
1167 case 0x14: // ???
1168 case 0x1A: // PCM-DATA
1169 return adpcm.peekReg(rg, time);
1170
1171 case 0x19: { // I/O DATA
1172 uint8_t input = periphery.read(time);
1173 uint8_t output = reg[0x19];
1174 uint8_t enable = reg[0x18];
1175 return (output & enable) | (input & ~enable) | 0xF0;
1176 }
1177 default:
1178 return reg[rg];
1179 }
1180}
1181
1182uint8_t Y8950::readStatus(EmuTime::param time) const
1183{
1184 return peekStatus(time);
1185}
1186
1187uint8_t Y8950::peekStatus(EmuTime::param time) const
1188{
1189 const_cast<Y8950Adpcm&>(adpcm).sync(time);
1190 return (status & (0x87 | statusMask)) | 0x06; // bit 1 and 2 are always 1
1191}
1192
1193void Y8950::callback(uint8_t flag)
1194{
1195 setStatus(flag);
1196}
1197
1198void Y8950::setStatus(uint8_t flags)
1199{
1200 status |= flags;
1201 if (status & statusMask) {
1202 status |= 0x80;
1203 irq.set();
1204 }
1205}
1206void Y8950::resetStatus(uint8_t flags)
1207{
1208 status &= ~flags;
1209 if (!(status & statusMask)) {
1210 status &= 0x7f;
1211 irq.reset();
1212 }
1213}
1215{
1216 return status;
1217}
1218void Y8950::changeStatusMask(uint8_t newMask)
1219{
1220 statusMask = newMask;
1221 status &= 0x87 | statusMask;
1222 if (status & statusMask) {
1223 status |= 0x80;
1224 irq.set();
1225 } else {
1226 status &= 0x7f;
1227 irq.reset();
1228 }
1229}
1230
1231
1232template<typename Archive>
1233void Y8950::Patch::serialize(Archive& ar, unsigned /*version*/)
1234{
1235 ar.serialize("AM", AM,
1236 "PM", PM,
1237 "EG", EG,
1238 "KR", KR,
1239 "ML", ML,
1240 "KL", KL,
1241 "TL", TL,
1242 "FB", FB,
1243 "AR", AR,
1244 "DR", DR,
1245 "SL", SL,
1246 "RR", RR);
1247}
1248
1249static constexpr std::initializer_list<enum_string<Y8950::EnvelopeState>> envelopeStateInfo = {
1250 { "ATTACK", Y8950::EnvelopeState::ATTACK },
1251 { "DECAY", Y8950::EnvelopeState::DECAY },
1252 { "SUSTAIN", Y8950::EnvelopeState::SUSTAIN },
1253 { "RELEASE", Y8950::EnvelopeState::RELEASE },
1254 { "FINISH", Y8950::EnvelopeState::FINISH }
1255};
1257
1258// version 1: initial version
1259// version 2: 'slotStatus' is replaced with 'key' and no longer serialized
1260// instead it's recalculated via update_key_status()
1261// version 3: serialize 'eg_mode' as an enum instead of an int, also merged
1262// the 2 enum values SUSHOLD and SUSTINE into SUSTAIN
1263template<typename Archive>
1264void Y8950::Slot::serialize(Archive& ar, unsigned version)
1265{
1266 ar.serialize("feedback", feedback,
1267 "output", output,
1268 "phase", phase,
1269 "eg_phase", eg_phase,
1270 "patch", patch);
1271 if (ar.versionAtLeast(version, 3)) {
1272 ar.serialize("eg_mode", eg_mode);
1273 } else {
1274 assert(Archive::IS_LOADER);
1275 int tmp = 0; // dummy init to avoid warning
1276 ar.serialize("eg_mode", tmp);
1277 switch (tmp) {
1278 using enum EnvelopeState;
1279 case 0: eg_mode = ATTACK; break;
1280 case 1: eg_mode = DECAY; break;
1281 case 2: eg_mode = SUSTAIN; break; // was SUSHOLD
1282 case 3: eg_mode = SUSTAIN; break; // was SUSTINE
1283 case 4: eg_mode = RELEASE; break;
1284 default: eg_mode = FINISH; break;
1285 }
1286 }
1287
1288 // These are restored by call to updateAll() in Y8950::Channel::serialize()
1289 // dPhase, tll, dPhaseARTableRks, dPhaseDRTableRks, eg_dPhase
1290 // These are restored by update_key_status():
1291 // key
1292}
1293
1294template<typename Archive>
1295void Y8950::Channel::serialize(Archive& ar, unsigned /*version*/)
1296{
1297 ar.serialize("mod", slot[MOD],
1298 "car", slot[CAR],
1299 "freq", freq,
1300 "alg", alg);
1301
1302 if constexpr (Archive::IS_LOADER) {
1303 slot[MOD].updateAll(freq);
1304 slot[CAR].updateAll(freq);
1305 }
1306}
1307
1308template<typename Archive>
1309void Y8950::serialize(Archive& ar, unsigned /*version*/)
1310{
1311 ar.serialize("keyboardConnector", connector,
1312 "adpcm", adpcm,
1313 "timer1", *timer1,
1314 "timer2", *timer2,
1315 "irq", irq);
1316 ar.serialize_blob("registers", reg);
1317 ar.serialize("pm_phase", pm_phase,
1318 "am_phase", am_phase,
1319 "noise_seed", noise_seed,
1320 "noiseA_phase", noiseA_phase,
1321 "noiseB_phase", noiseB_phase,
1322 "noiseA_dphase", noiseA_dPhase,
1323 "noiseB_dphase", noiseB_dPhase,
1324 "channels", ch,
1325 "status", status,
1326 "statusMask", statusMask,
1327 "rythm_mode", rythm_mode,
1328 "am_mode", am_mode,
1329 "pm_mode", pm_mode,
1330 "enabled", enabled);
1331
1332 if constexpr (Archive::IS_LOADER) {
1333 // TODO restore more state from registers
1334 static constexpr std::array<uint8_t, 2> rewriteRegs = {
1335 6, // connector
1336 15, // dac13
1337 };
1338
1339 update_key_status();
1340 EmuTime::param time = motherBoard.getCurrentTime();
1341 for (auto r : rewriteRegs) {
1342 writeReg(r, reg[r], time);
1343 }
1344 }
1345}
1346
1347
1348// SimpleDebuggable
1349
1351 const std::string& name_)
1352 : SimpleDebuggable(motherBoard_, name_ + " regs", "MSX-AUDIO", 0x100)
1353{
1354}
1355
1356uint8_t Y8950::Debuggable::read(unsigned address, EmuTime::param time)
1357{
1358 const auto& y8950 = OUTER(Y8950, debuggable);
1359 return y8950.peekReg(narrow<uint8_t>(address), time);
1360}
1361
1362void Y8950::Debuggable::write(unsigned address, uint8_t value, EmuTime::param time)
1363{
1364 auto& y8950 = OUTER(Y8950, debuggable);
1365 y8950.writeReg(narrow<uint8_t>(address), value, time);
1366}
1367
1370
1371} // namespace openmsx
void writeDAC(int16_t value, EmuTime::param time)
void set()
Set the interrupt request on the bus.
Definition IRQHelper.hh:76
void reset()
Reset the interrupt request on the bus.
Definition IRQHelper.hh:85
EmuTime::param getCurrentTime() const
Convenience method: This is the same as getScheduler().getCurrentTime().
void updateStream(EmuTime::param time)
void unregisterSound()
Unregisters this sound device with the Mixer.
void registerSound(const DeviceConfig &config)
Registers this sound device with the Mixer.
byte readReg(byte rg, EmuTime::param time)
bool isMuted() const
Definition Y8950Adpcm.cc:94
byte peekReg(byte rg, EmuTime::param time) const
void writeReg(byte rg, byte data, EmuTime::param time)
void reset(EmuTime::param time)
Definition Y8950Adpcm.cc:68
void write(byte data, EmuTime::param time)
byte peek(EmuTime::param time) const
virtual void write(nibble outputs, nibble values, EmuTime::param time)=0
Write to (some of) the pins.
virtual void setSPOFF(bool value, EmuTime::param time)
SP-OFF bit (bit 3 in Y8950 register 7)
virtual nibble read(EmuTime::param time)=0
Read from (some of) the pins Some of the pins might be programmed as output, but this method doesn't ...
void reset(EmuTime::param time)
Definition Y8950.cc:558
void setStatus(uint8_t flags)
Definition Y8950.cc:1198
uint8_t peekReg(uint8_t rg, EmuTime::param time) const
Definition Y8950.cc:1159
void setEnabled(bool enabled, EmuTime::param time)
Definition Y8950.cc:782
static constexpr int CLOCK_FREQ_DIV
Definition Y8950.hh:31
static constexpr int R04_IRQ_RESET
Definition Y8950.hh:49
uint8_t peekStatus(EmuTime::param time) const
Definition Y8950.cc:1187
uint8_t readStatus(EmuTime::param time) const
Definition Y8950.cc:1182
FixedPoint< EG_DP_BITS - EG_BITS > EnvPhaseIndex
Definition Y8950.hh:109
static constexpr int R04_ST2
Definition Y8950.hh:37
void serialize(Archive &ar, unsigned version)
Definition Y8950.cc:1309
void clearRam()
Definition Y8950.cc:552
static constexpr int R04_ST1
Definition Y8950.hh:35
void writeReg(uint8_t rg, uint8_t data, EmuTime::param time)
Definition Y8950.cc:905
static constexpr int CLOCK_FREQ
Definition Y8950.hh:30
static constexpr int EG_BITS
Definition Y8950.hh:105
void resetStatus(uint8_t flags)
Definition Y8950.cc:1206
uint8_t readReg(uint8_t rg, EmuTime::param time)
Definition Y8950.cc:1144
uint8_t peekRawStatus() const
Definition Y8950.cc:1214
constexpr auto enumerate(Iterable &&iterable)
Heavily inspired by Nathan Reed's blog post: Python-Like enumerate() In C++17 http://reedbeta....
Definition enumerate.hh:28
int main(int argc, char **argv)
Definition main.cc:175
constexpr double pi
Definition Math.hh:24
int16_t clipToInt16(T x)
Clip x to range [-32768,32767].
Definition Math.hh:47
constexpr double e
Definition Math.hh:21
constexpr double round(double x)
Definition cstd.hh:247
This file implemented 3 utility functions:
Definition Autofire.cc:11
constexpr void fill(ForwardRange &&range, const T &value)
Definition ranges.hh:315
#define OUTER(type, member)
Definition outer.hh:42
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
#define SERIALIZE_ENUM(TYPE, INFO)
#define SERIALIZE_CLASS_VERSION(CLASS, VERSION)
constexpr auto xrange(T e)
Definition xrange.hh:132