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 int 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 = 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 case ATTACK:
394 eg_dPhase = dPhaseARTableRks[patch.AR];
395 break;
396 case DECAY:
397 eg_dPhase = dPhaseDRTableRks[patch.DR];
398 break;
399 case SUSTAIN:
400 case RELEASE:
401 eg_dPhase = dPhaseDRTableRks[patch.RR];
402 break;
403 case FINISH:
404 eg_dPhase = Y8950::EnvPhaseIndex(0);
405 break;
406 }
407}
408
409void Y8950::Slot::updateAll(unsigned freq)
410{
411 updatePG(freq);
412 updateTLL(freq);
413 updateRKS(freq);
414 updateEG(); // EG should be last
415}
416
417bool Y8950::Slot::isActive() const
418{
419 return eg_mode != FINISH;
420}
421
422// Slot key on
423void Y8950::Slot::slotOn(KeyPart part)
424{
425 if (!key) {
426 eg_mode = ATTACK;
427 phase = 0;
428 eg_phase = Y8950::EnvPhaseIndex(adjustRA[eg_phase.toInt()]);
429 }
430 key |= part;
431}
432
433// Slot key off
434void Y8950::Slot::slotOff(KeyPart part)
435{
436 if (key) {
437 key &= ~part;
438 if (!key) {
439 if (eg_mode == ATTACK) {
440 eg_phase = Y8950::EnvPhaseIndex(adjustAR[eg_phase.toInt()]);
441 }
442 eg_mode = RELEASE;
443 }
444 }
445}
446
447
448// class Y8950::Channel
449
450Y8950::Channel::Channel()
451{
452 reset();
453}
454
455void Y8950::Channel::reset()
456{
457 setFreq(0);
458 slot[MOD].reset();
459 slot[CAR].reset();
460 alg = false;
461}
462
463// Set frequency (combined F-Number (10bit) and Block (3bit))
464void Y8950::Channel::setFreq(unsigned freq_)
465{
466 freq = freq_;
467}
468
469void Y8950::Channel::keyOn(KeyPart part)
470{
471 slot[MOD].slotOn(part);
472 slot[CAR].slotOn(part);
473}
474
475void Y8950::Channel::keyOff(KeyPart part)
476{
477 slot[MOD].slotOff(part);
478 slot[CAR].slotOff(part);
479}
480
481static constexpr auto INPUT_RATE = unsigned(cstd::round(Y8950::CLOCK_FREQ / double(Y8950::CLOCK_FREQ_DIV)));
482
483Y8950::Y8950(const std::string& name_, const DeviceConfig& config,
484 unsigned sampleRam, EmuTime::param time, MSXAudio& audio)
485 : ResampledSoundDevice(config.getMotherBoard(), name_, "MSX-AUDIO", 9 + 5 + 1, INPUT_RATE, false)
486 , motherBoard(config.getMotherBoard())
487 , periphery(audio.createPeriphery(getName()))
488 , adpcm(*this, config, name_, sampleRam)
489 , connector(motherBoard.getPluggingController())
490 , dac13(name_ + " DAC", "MSX-AUDIO 13-bit DAC", config)
491 , debuggable(motherBoard, getName())
492 , timer1(EmuTimer::createOPL3_1(motherBoard.getScheduler(), *this))
493 , timer2(EmuTimer::createOPL3_2(motherBoard.getScheduler(), *this))
494 , irq(motherBoard, getName() + ".IRQ")
495{
496 // For debugging: print out tables to be able to compare before/after
497 // when the calculation changes.
498 if (false) {
499 for (auto i : xrange(PM_PG_WIDTH)) {
500 std::cout << pmTable[0][i] << ' '
501 << pmTable[1][i] << '\n';
502 }
503 std::cout << '\n';
504
505 for (auto i : xrange(EG_MUTE)) {
506 std::cout << adjustRA[i] << ' '
507 << adjustAR[i] << '\n';
508 }
509 std::cout << adjustRA[EG_MUTE] << "\n\n";
510
511 for (const auto& e : dB2LinTab) std::cout << e << '\n';
512 std::cout << '\n';
513
514 for (auto i : xrange(16 * 8)) {
515 for (auto j : xrange(4)) {
516 std::cout << tllTable[i][j] << ' ';
517 }
518 std::cout << '\n';
519 }
520 std::cout << '\n';
521
522 for (const auto& e : sinTable) std::cout << e << '\n';
523 std::cout << '\n';
524
525 for (auto i : xrange(16)) {
526 for (auto j : xrange(16)) {
527 std::cout << dPhaseArTable[i][j].getRawValue() << ' ';
528 }
529 std::cout << '\n';
530 }
531 std::cout << '\n';
532
533 for (auto i : xrange(16)) {
534 for (auto j : xrange(16)) {
535 std::cout << dPhaseDrTable[i][j].getRawValue() << ' ';
536 }
537 std::cout << '\n';
538 }
539 std::cout << '\n';
540 }
541
542 reset(time);
543 registerSound(config);
544}
545
550
552{
553 adpcm.clearRam();
554}
555
556// Reset whole of opl except patch data.
557void Y8950::reset(EmuTime::param time)
558{
559 for (auto& c : ch) c.reset();
560
561 rythm_mode = false;
562 am_mode = false;
563 pm_mode = false;
564 pm_phase = 0;
565 am_phase = 0;
566 noise_seed = 0xffff;
567 noiseA_phase = 0;
568 noiseB_phase = 0;
569 noiseA_dPhase = 0;
570 noiseB_dPhase = 0;
571
572 // update the output buffer before changing the register
573 updateStream(time);
574 ranges::fill(reg, 0x00);
575
576 reg[0x04] = 0x18;
577 reg[0x19] = 0x0F; // fixes 'Thunderbirds are Go'
578 status = 0x00;
579 statusMask = 0;
580 irq.reset();
581
582 adpcm.reset(time);
583}
584
585
586// Drum key on
587void Y8950::keyOn_BD() { ch[6].keyOn(KEY_RHYTHM); }
588void Y8950::keyOn_HH() { ch[7].slot[MOD].slotOn(KEY_RHYTHM); }
589void Y8950::keyOn_SD() { ch[7].slot[CAR].slotOn(KEY_RHYTHM); }
590void Y8950::keyOn_TOM() { ch[8].slot[MOD].slotOn(KEY_RHYTHM); }
591void Y8950::keyOn_CYM() { ch[8].slot[CAR].slotOn(KEY_RHYTHM); }
592
593// Drum key off
594void Y8950::keyOff_BD() { ch[6].keyOff(KEY_RHYTHM); }
595void Y8950::keyOff_HH() { ch[7].slot[MOD].slotOff(KEY_RHYTHM); }
596void Y8950::keyOff_SD() { ch[7].slot[CAR].slotOff(KEY_RHYTHM); }
597void Y8950::keyOff_TOM(){ ch[8].slot[MOD].slotOff(KEY_RHYTHM); }
598void Y8950::keyOff_CYM(){ ch[8].slot[CAR].slotOff(KEY_RHYTHM); }
599
600// Change Rhythm Mode
601void Y8950::setRythmMode(int data)
602{
603 bool newMode = (data & 32) != 0;
604 if (rythm_mode != newMode) {
605 rythm_mode = newMode;
606 if (!rythm_mode) {
607 // ON->OFF
608 keyOff_BD(); // TODO keyOff() or immediately to FINISH?
609 keyOff_HH(); // other variants use keyOff(), but
610 keyOff_SD(); // verify on real HW
611 keyOff_TOM();
612 keyOff_CYM();
613 }
614 }
615}
616
617// recalculate 'key' from register settings
618void Y8950::update_key_status()
619{
620 for (auto [i, c] : enumerate(ch)) {
621 uint8_t main = (reg[0xb0 + i] & 0x20) ? KEY_MAIN : 0;
622 c.slot[MOD].key = main;
623 c.slot[CAR].key = main;
624 }
625 if (rythm_mode) {
626 ch[6].slot[MOD].key |= uint8_t((reg[0xbd] & 0x10) ? KEY_RHYTHM : 0); // BD1
627 ch[6].slot[CAR].key |= uint8_t((reg[0xbd] & 0x10) ? KEY_RHYTHM : 0); // BD2
628 ch[7].slot[MOD].key |= uint8_t((reg[0xbd] & 0x01) ? KEY_RHYTHM : 0); // HH
629 ch[7].slot[CAR].key |= uint8_t((reg[0xbd] & 0x08) ? KEY_RHYTHM : 0); // SD
630 ch[8].slot[MOD].key |= uint8_t((reg[0xbd] & 0x04) ? KEY_RHYTHM : 0); // TOM
631 ch[8].slot[CAR].key |= uint8_t((reg[0xbd] & 0x02) ? KEY_RHYTHM : 0); // CYM
632 }
633}
634
635
636//
637// Generate wave data
638//
639
640// Convert Amp(0 to EG_HEIGHT) to Phase(0 to 8PI).
641static constexpr int wave2_8pi(int e)
642{
643 int shift = SLOT_AMP_BITS - PG_BITS - 2;
644 return (shift > 0) ? (e >> shift) : (e << -shift);
645}
646
647unsigned Y8950::Slot::calc_phase(int lfo_pm)
648{
649 if (patch.PM) {
650 phase += (dPhase * lfo_pm) >> PM_AMP_BITS;
651 } else {
652 phase += dPhase;
653 }
654 return phase >> DP_BASE_BITS;
655}
656
657static constexpr auto S2E(int x) {
658 return Y8950::EnvPhaseIndex(int(x / EG_STEP));
659}
660static constexpr std::array<Y8950::EnvPhaseIndex, 16> SL = {
661 S2E( 0), S2E( 3), S2E( 6), S2E( 9), S2E(12), S2E(15), S2E(18), S2E(21),
662 S2E(24), S2E(27), S2E(30), S2E(33), S2E(36), S2E(39), S2E(42), S2E(93)
663};
664unsigned Y8950::Slot::calc_envelope(int lfo_am)
665{
666 unsigned egOut = 0;
667 switch (eg_mode) {
668 case ATTACK:
669 eg_phase += eg_dPhase;
670 if (eg_phase >= EG_DP_MAX) {
671 egOut = 0;
672 eg_phase = Y8950::EnvPhaseIndex(0);
673 eg_mode = DECAY;
674 updateEG();
675 } else {
676 egOut = adjustAR[eg_phase.toInt()];
677 }
678 break;
679
680 case DECAY:
681 eg_phase += eg_dPhase;
682 if (eg_phase >= SL[patch.SL]) {
683 eg_phase = SL[patch.SL];
684 eg_mode = SUSTAIN;
685 updateEG();
686 }
687 egOut = eg_phase.toInt();
688 break;
689
690 case SUSTAIN:
691 if (!patch.EG) {
692 eg_phase += eg_dPhase;
693 }
694 egOut = eg_phase.toInt();
695 if (egOut >= EG_MUTE) {
696 eg_phase = EG_DP_MAX;
697 eg_mode = FINISH;
698 egOut = EG_MUTE - 1;
699 }
700 break;
701
702 case RELEASE:
703 eg_phase += eg_dPhase;
704 egOut = eg_phase.toInt();
705 if (egOut >= EG_MUTE) {
706 eg_phase = EG_DP_MAX;
707 eg_mode = FINISH;
708 egOut = EG_MUTE - 1;
709 }
710 break;
711
712 case FINISH:
713 egOut = EG_MUTE - 1;
714 break;
715 }
716
717 egOut = ((egOut + tll) * EG_PER_DB);
718 if (patch.AM) {
719 egOut += lfo_am;
720 }
721 return std::min<unsigned>(egOut, DB_MUTE - 1);
722}
723
724int Y8950::Slot::calc_slot_car(int lfo_pm, int lfo_am, int fm)
725{
726 unsigned egOut = calc_envelope(lfo_am);
727 int pgout = narrow<int>(calc_phase(lfo_pm)) + wave2_8pi(fm);
728 return dB2LinTab[sinTable[pgout & PG_MASK] + egOut];
729}
730
731int Y8950::Slot::calc_slot_mod(int lfo_pm, int lfo_am)
732{
733 unsigned egOut = calc_envelope(lfo_am);
734 unsigned pgout = calc_phase(lfo_pm);
735
736 if (patch.FB != 0) {
737 pgout += wave2_8pi(feedback) >> patch.FB;
738 }
739 int newOutput = dB2LinTab[sinTable[pgout & PG_MASK] + egOut];
740 feedback = (output + newOutput) >> 1;
741 output = newOutput;
742 return feedback;
743}
744
745int Y8950::Slot::calc_slot_tom(int lfo_pm, int lfo_am)
746{
747 unsigned egOut = calc_envelope(lfo_am);
748 unsigned pgout = calc_phase(lfo_pm);
749 return dB2LinTab[sinTable[pgout & PG_MASK] + egOut];
750}
751
752int Y8950::Slot::calc_slot_snare(int lfo_pm, int lfo_am, int whiteNoise)
753{
754 unsigned egOut = calc_envelope(lfo_am);
755 unsigned pgout = calc_phase(lfo_pm);
756 unsigned tmp = (pgout & (1 << (PG_BITS - 1))) ? 0 : 2 * DB_MUTE;
757 return (dB2LinTab[tmp + egOut] + dB2LinTab[egOut + whiteNoise]) >> 1;
758}
759
760int Y8950::Slot::calc_slot_cym(int lfo_am, int a, int b)
761{
762 unsigned egOut = calc_envelope(lfo_am);
763 return (dB2LinTab[egOut + a] + dB2LinTab[egOut + b]) >> 1;
764}
765
766// HI-HAT
767int Y8950::Slot::calc_slot_hat(int lfo_am, int a, int b, int whiteNoise)
768{
769 unsigned egOut = calc_envelope(lfo_am);
770 return (dB2LinTab[egOut + whiteNoise] +
771 dB2LinTab[egOut + a] +
772 dB2LinTab[egOut + b]) >> 2;
773}
774
775float Y8950::getAmplificationFactorImpl() const
776{
777 return 1.0f / (1 << DB2LIN_AMP_BITS);
778}
779
780void Y8950::setEnabled(bool enabled_, EmuTime::param time)
781{
782 updateStream(time);
783 enabled = enabled_;
784}
785
786bool Y8950::checkMuteHelper()
787{
788 if (!enabled) {
789 return true;
790 }
791 for (auto i : xrange(6)) {
792 if (ch[i].slot[CAR].isActive()) return false;
793 }
794 if (!rythm_mode) {
795 for (auto i : xrange(6, 9)) {
796 if (ch[i].slot[CAR].isActive()) return false;
797 }
798 } else {
799 if (ch[6].slot[CAR].isActive()) return false;
800 if (ch[7].slot[MOD].isActive()) return false;
801 if (ch[7].slot[CAR].isActive()) return false;
802 if (ch[8].slot[MOD].isActive()) return false;
803 if (ch[8].slot[CAR].isActive()) return false;
804 }
805
806 return adpcm.isMuted();
807}
808
809void Y8950::generateChannels(std::span<float*> bufs, unsigned num)
810{
811 // TODO implement per-channel mute (instead of all-or-nothing)
812 if (checkMuteHelper()) {
813 // TODO update internal state even when muted
814 // during mute pm_phase, am_phase, noiseA_phase, noiseB_phase
815 // and noise_seed aren't updated, probably ok
816 ranges::fill(bufs, nullptr);
817 return;
818 }
819
820 for (auto sample : xrange(num)) {
821 // Amplitude modulation: 27 output levels (triangle waveform);
822 // 1 level takes one of: 192, 256 or 448 samples
823 // One entry from LFO_AM_TABLE lasts for 64 samples
824 // lfo_am_table is 210 elements long
825 ++am_phase;
826 if (am_phase == (LFO_AM_TAB_ELEMENTS * 64)) am_phase = 0;
827 int tmp = narrow_cast<int>(lfo_am_table[am_phase / 64]);
828 int lfo_am = am_mode ? tmp : tmp / 4;
829
830 pm_phase = (pm_phase + PM_DPHASE) & (PM_DP_WIDTH - 1);
831 int lfo_pm = pmTable[pm_mode][pm_phase >> (PM_DP_BITS - PM_PG_BITS)];
832
833 if (noise_seed & 1) {
834 noise_seed ^= 0x24000;
835 }
836 noise_seed >>= 1;
837 int whiteNoise = noise_seed & 1 ? DB_POS(6) : DB_NEG(6);
838
839 noiseA_phase += noiseA_dPhase;
840 noiseA_phase &= (0x40 << 11) - 1;
841 if ((noiseA_phase >> 11) == 0x3f) {
842 noiseA_phase = 0;
843 }
844 int noiseA = noiseA_phase & (0x03 << 11) ? DB_POS(6) : DB_NEG(6);
845
846 noiseB_phase += noiseB_dPhase;
847 noiseB_phase &= (0x10 << 11) - 1;
848 int noiseB = noiseB_phase & (0x0A << 11) ? DB_POS(6) : DB_NEG(6);
849
850 for (auto i : xrange(rythm_mode ? 6 : 9)) {
851 if (ch[i].slot[CAR].isActive()) {
852 bufs[i][sample] += narrow_cast<float>(ch[i].alg
853 ? ch[i].slot[CAR].calc_slot_car(lfo_pm, lfo_am, 0) +
854 ch[i].slot[MOD].calc_slot_mod(lfo_pm, lfo_am)
855 : ch[i].slot[CAR].calc_slot_car(lfo_pm, lfo_am,
856 ch[i].slot[MOD].calc_slot_mod(lfo_pm, lfo_am)));
857 } else {
858 //bufs[i][sample] += 0;
859 }
860 }
861 if (rythm_mode) {
862 //bufs[6][sample] += 0;
863 //bufs[7][sample] += 0;
864 //bufs[8][sample] += 0;
865
866 // TODO wasn't in original source either
867 (void)ch[7].slot[MOD].calc_phase(lfo_pm);
868 (void)ch[8].slot[CAR].calc_phase(lfo_pm);
869
870 bufs[ 9][sample] += (ch[6].slot[CAR].isActive())
871 ? narrow_cast<float>(
872 2 * ch[6].slot[CAR].calc_slot_car(lfo_pm, lfo_am,
873 ch[6].slot[MOD].calc_slot_mod(lfo_pm, lfo_am)))
874 : 0.0f;
875 bufs[10][sample] += (ch[7].slot[CAR].isActive())
876 ? narrow_cast<float>(2 * ch[7].slot[CAR].calc_slot_snare(lfo_pm, lfo_am, whiteNoise))
877 : 0.0f;
878 bufs[11][sample] += (ch[8].slot[CAR].isActive())
879 ? narrow_cast<float>(2 * ch[8].slot[CAR].calc_slot_cym(lfo_am, noiseA, noiseB))
880 : 0.0f;
881 bufs[12][sample] += (ch[7].slot[MOD].isActive())
882 ? narrow_cast<float>(2 * ch[7].slot[MOD].calc_slot_hat(lfo_am, noiseA, noiseB, whiteNoise))
883 : 0.0f;
884 bufs[13][sample] += (ch[8].slot[MOD].isActive())
885 ? narrow_cast<float>(2 * ch[8].slot[MOD].calc_slot_tom(lfo_pm, lfo_am))
886 : 0.0f;
887 } else {
888 //bufs[ 9] += 0;
889 //bufs[10] += 0;
890 //bufs[11] += 0;
891 //bufs[12] += 0;
892 //bufs[13] += 0;
893 }
894
895 bufs[14][sample] += narrow_cast<float>(adpcm.calcSample());
896 }
897}
898
899//
900// I/O Ctrl
901//
902
903void Y8950::writeReg(uint8_t rg, uint8_t data, EmuTime::param time)
904{
905 static constexpr std::array<int, 32> sTbl = {
906 0, 2, 4, 1, 3, 5, -1, -1,
907 6, 8, 10, 7, 9, 11, -1, -1,
908 12, 14, 16, 13, 15, 17, -1, -1,
909 -1, -1, -1, -1, -1, -1, -1, -1
910 };
911
912 // TODO only for registers that influence sound
913 // TODO also ADPCM
914 //if (rg >= 0x20) {
915 // update the output buffer before changing the register
916 updateStream(time);
917 //}
918
919 switch (rg & 0xe0) {
920 case 0x00: {
921 switch (rg) {
922 case 0x01: // TEST
923 // TODO
924 // Y8950 MSX-AUDIO Test register $01 (write only)
925 //
926 // Bit Description
927 //
928 // 7 Reset LFOs - seems to force the LFOs to their initial
929 // values (eg. maximum amplitude, zero phase deviation)
930 //
931 // 6 something to do with ADPCM - bit 0 of the status
932 // register is affected by setting this bit (PCM BSY)
933 //
934 // 5 No effect? - Waveform select enable in YM3812 OPL2 so seems
935 // reasonable that this bit wouldn't have been used in OPL
936 //
937 // 4 No effect?
938 //
939 // 3 Faster LFOs - increases the frequencies of the LFOs and
940 // (maybe) the timers (cf. YM2151 test register)
941 //
942 // 2 Reset phase generators - No phase generator output, but
943 // envelope generators still work (can hear a transient
944 // when they are gated)
945 //
946 // 1 No effect?
947 //
948 // 0 Reset envelopes - Envelope generator outputs forced
949 // to maximum, so all enabled voices sound at maximum
950 reg[rg] = data;
951 break;
952
953 case 0x02: // TIMER1 (resolution 80us)
954 timer1->setValue(data);
955 reg[rg] = data;
956 break;
957
958 case 0x03: // TIMER2 (resolution 320us)
959 timer2->setValue(data);
960 reg[rg] = data;
961 break;
962
963 case 0x04: // FLAG CONTROL
964 if (data & Y8950::R04_IRQ_RESET) {
965 resetStatus(0x78); // reset all flags
966 } else {
967 changeStatusMask((~data) & 0x78);
968 timer1->setStart((data & Y8950::R04_ST1) != 0, time);
969 timer2->setStart((data & Y8950::R04_ST2) != 0, time);
970 reg[rg] = data;
971 }
972 adpcm.resetStatus();
973 break;
974
975 case 0x06: // (KEYBOARD OUT)
976 connector.write(data, time);
977 reg[rg] = data;
978 break;
979
980 case 0x07: // START/REC/MEM DATA/REPEAT/SP-OFF/-/-/RESET
981 periphery.setSPOFF((data & 8) != 0, time); // bit 3
982 [[fallthrough]];
983
984 case 0x08: // CSM/KEY BOARD SPLIT/-/-/SAMPLE/DA AD/64K/ROM
985 case 0x09: // START ADDRESS (L)
986 case 0x0A: // START ADDRESS (H)
987 case 0x0B: // STOP ADDRESS (L)
988 case 0x0C: // STOP ADDRESS (H)
989 case 0x0D: // PRESCALE (L)
990 case 0x0E: // PRESCALE (H)
991 case 0x0F: // ADPCM-DATA
992 case 0x10: // DELTA-N (L)
993 case 0x11: // DELTA-N (H)
994 case 0x12: // ENVELOP CONTROL
995 case 0x1A: // PCM-DATA
996 reg[rg] = data;
997 adpcm.writeReg(rg, data, time);
998 break;
999
1000 case 0x15: // DAC-DATA (bit9-2)
1001 reg[rg] = data;
1002 if (reg[0x08] & 0x04) {
1003 int tmp = static_cast<signed char>(reg[0x15]) * 256
1004 + reg[0x16];
1005 tmp = (tmp * 4) >> (7 - reg[0x17]);
1006 dac13.writeDAC(Math::clipToInt16(tmp), time);
1007 }
1008 break;
1009 case 0x16: // (bit1-0)
1010 reg[rg] = data & 0xC0;
1011 break;
1012 case 0x17: // (exponent)
1013 reg[rg] = data & 0x07;
1014 break;
1015
1016 case 0x18: // I/O-CONTROL (bit3-0)
1017 // 0 -> input
1018 // 1 -> output
1019 reg[rg] = data;
1020 periphery.write(reg[0x18], reg[0x19], time);
1021 break;
1022
1023 case 0x19: // I/O-DATA (bit3-0)
1024 reg[rg] = data;
1025 periphery.write(reg[0x18], reg[0x19], time);
1026 break;
1027 }
1028 break;
1029 }
1030 case 0x20: {
1031 int s = sTbl[rg & 0x1f];
1032 if (s >= 0) {
1033 auto& chan = ch[s / 2];
1034 auto& slot = chan.slot[s & 1];
1035 slot.patch.AM = (data >> 7) & 1;
1036 slot.patch.PM = (data >> 6) & 1;
1037 slot.patch.EG = (data >> 5) & 1;
1038 slot.patch.setKeyScaleRate((data & 0x10) != 0);
1039 slot.patch.ML = (data >> 0) & 15;
1040 slot.updateAll(chan.freq);
1041 }
1042 reg[rg] = data;
1043 break;
1044 }
1045 case 0x40: {
1046 int s = sTbl[rg & 0x1f];
1047 if (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 int s = sTbl[rg & 0x1f];
1059 if (s >= 0) {
1060 auto& slot = ch[s / 2].slot[s & 1];
1061 slot.patch.AR = (data >> 4) & 15;
1062 slot.patch.DR = (data >> 0) & 15;
1063 slot.updateEG();
1064 }
1065 reg[rg] = data;
1066 break;
1067 }
1068 case 0x80: {
1069 int s = sTbl[rg & 0x1f];
1070 if (s >= 0) {
1071 auto& slot = ch[s / 2].slot[s & 1];
1072 slot.patch.SL = (data >> 4) & 15;
1073 slot.patch.RR = (data >> 0) & 15;
1074 slot.updateEG();
1075 }
1076 reg[rg] = data;
1077 break;
1078 }
1079 case 0xa0: {
1080 if (rg == 0xbd) {
1081 am_mode = (data & 0x80) != 0;
1082 pm_mode = (data & 0x40) != 0;
1083
1084 setRythmMode(data);
1085 if (rythm_mode) {
1086 if (data & 0x10) keyOn_BD(); else keyOff_BD();
1087 if (data & 0x08) keyOn_SD(); else keyOff_SD();
1088 if (data & 0x04) keyOn_TOM(); else keyOff_TOM();
1089 if (data & 0x02) keyOn_CYM(); else keyOff_CYM();
1090 if (data & 0x01) keyOn_HH(); else keyOff_HH();
1091 }
1092 ch[6].slot[MOD].updateAll(ch[6].freq);
1093 ch[6].slot[CAR].updateAll(ch[6].freq);
1094 ch[7].slot[MOD].updateAll(ch[7].freq);
1095 ch[7].slot[CAR].updateAll(ch[7].freq);
1096 ch[8].slot[MOD].updateAll(ch[8].freq);
1097 ch[8].slot[CAR].updateAll(ch[8].freq);
1098
1099 reg[rg] = data;
1100 break;
1101 }
1102 unsigned c = rg & 0x0f;
1103 if (c > 8) {
1104 // 0xa9-0xaf 0xb9-0xbf
1105 break;
1106 }
1107 unsigned freq = [&] {
1108 if (!(rg & 0x10)) {
1109 // 0xa0-0xa8
1110 return data | ((reg[rg + 0x10] & 0x1F) << 8);
1111 } else {
1112 // 0xb0-0xb8
1113 if (data & 0x20) {
1114 ch[c].keyOn (KEY_MAIN);
1115 } else {
1116 ch[c].keyOff(KEY_MAIN);
1117 }
1118 return reg[rg - 0x10] | ((data & 0x1F) << 8);
1119 }
1120 }();
1121 ch[c].setFreq(freq);
1122 unsigned fNum = freq % 1024;
1123 unsigned block = freq / 1024;
1124 switch (c) {
1125 case 7: noiseA_dPhase = fNum << block;
1126 break;
1127 case 8: noiseB_dPhase = fNum << block;
1128 break;
1129 }
1130 ch[c].slot[CAR].updateAll(freq);
1131 ch[c].slot[MOD].updateAll(freq);
1132 reg[rg] = data;
1133 break;
1134 }
1135 case 0xc0: {
1136 if (rg > 0xc8)
1137 break;
1138 int c = rg - 0xC0;
1139 ch[c].slot[MOD].patch.setFeedbackShift((data >> 1) & 7);
1140 ch[c].alg = data & 1;
1141 reg[rg] = data;
1142 }
1143 }
1144}
1145
1146uint8_t Y8950::readReg(uint8_t rg, EmuTime::param time)
1147{
1148 updateStream(time); // TODO only when necessary
1149
1150 switch (rg) {
1151 case 0x0F: // ADPCM-DATA
1152 case 0x13: // ???
1153 case 0x14: // ???
1154 case 0x1A: // PCM-DATA
1155 return adpcm.readReg(rg, time);
1156 default:
1157 return peekReg(rg, time);
1158 }
1159}
1160
1161uint8_t Y8950::peekReg(uint8_t rg, EmuTime::param time) const
1162{
1163 switch (rg) {
1164 case 0x05: // (KEYBOARD IN)
1165 return connector.peek(time);
1166
1167 case 0x0F: // ADPCM-DATA
1168 case 0x13: // ???
1169 case 0x14: // ???
1170 case 0x1A: // PCM-DATA
1171 return adpcm.peekReg(rg, time);
1172
1173 case 0x19: { // I/O DATA
1174 uint8_t input = periphery.read(time);
1175 uint8_t output = reg[0x19];
1176 uint8_t enable = reg[0x18];
1177 return (output & enable) | (input & ~enable) | 0xF0;
1178 }
1179 default:
1180 return reg[rg];
1181 }
1182}
1183
1184uint8_t Y8950::readStatus(EmuTime::param time) const
1185{
1186 uint8_t result = peekStatus(time);
1187 //std::cout << "status: " << (int)result << '\n';
1188 return result;
1189}
1190
1191uint8_t Y8950::peekStatus(EmuTime::param time) const
1192{
1193 const_cast<Y8950Adpcm&>(adpcm).sync(time);
1194 return (status & (0x87 | statusMask)) | 0x06; // bit 1 and 2 are always 1
1195}
1196
1197void Y8950::callback(uint8_t flag)
1198{
1199 setStatus(flag);
1200}
1201
1202void Y8950::setStatus(uint8_t flags)
1203{
1204 status |= flags;
1205 if (status & statusMask) {
1206 status |= 0x80;
1207 irq.set();
1208 }
1209}
1210void Y8950::resetStatus(uint8_t flags)
1211{
1212 status &= ~flags;
1213 if (!(status & statusMask)) {
1214 status &= 0x7f;
1215 irq.reset();
1216 }
1217}
1219{
1220 return status;
1221}
1222void Y8950::changeStatusMask(uint8_t newMask)
1223{
1224 statusMask = newMask;
1225 status &= 0x87 | statusMask;
1226 if (status & statusMask) {
1227 status |= 0x80;
1228 irq.set();
1229 } else {
1230 status &= 0x7f;
1231 irq.reset();
1232 }
1233}
1234
1235
1236template<typename Archive>
1237void Y8950::Patch::serialize(Archive& ar, unsigned /*version*/)
1238{
1239 ar.serialize("AM", AM,
1240 "PM", PM,
1241 "EG", EG,
1242 "KR", KR,
1243 "ML", ML,
1244 "KL", KL,
1245 "TL", TL,
1246 "FB", FB,
1247 "AR", AR,
1248 "DR", DR,
1249 "SL", SL,
1250 "RR", RR);
1251}
1252
1253static constexpr std::initializer_list<enum_string<Y8950::EnvelopeState>> envelopeStateInfo = {
1254 { "ATTACK", Y8950::ATTACK },
1255 { "DECAY", Y8950::DECAY },
1256 { "SUSTAIN", Y8950::SUSTAIN },
1257 { "RELEASE", Y8950::RELEASE },
1258 { "FINISH", Y8950::FINISH }
1259};
1261
1262// version 1: initial version
1263// version 2: 'slotStatus' is replaced with 'key' and no longer serialized
1264// instead it's recalculated via update_key_status()
1265// version 3: serialize 'eg_mode' as an enum instead of an int, also merged
1266// the 2 enum values SUSHOLD and SUSTINE into SUSTAIN
1267template<typename Archive>
1268void Y8950::Slot::serialize(Archive& ar, unsigned version)
1269{
1270 ar.serialize("feedback", feedback,
1271 "output", output,
1272 "phase", phase,
1273 "eg_phase", eg_phase,
1274 "patch", patch);
1275 if (ar.versionAtLeast(version, 3)) {
1276 ar.serialize("eg_mode", eg_mode);
1277 } else {
1278 assert(Archive::IS_LOADER);
1279 int tmp = 0; // dummy init to avoid warning
1280 ar.serialize("eg_mode", tmp);
1281 switch (tmp) {
1282 case 0: eg_mode = ATTACK; break;
1283 case 1: eg_mode = DECAY; break;
1284 case 2: eg_mode = SUSTAIN; break; // was SUSHOLD
1285 case 3: eg_mode = SUSTAIN; break; // was SUSTINE
1286 case 4: eg_mode = RELEASE; break;
1287 default: eg_mode = FINISH; break;
1288 }
1289 }
1290
1291 // These are restored by call to updateAll() in Y8950::Channel::serialize()
1292 // dPhase, tll, dPhaseARTableRks, dPhaseDRTableRks, eg_dPhase
1293 // These are restored by update_key_status():
1294 // key
1295}
1296
1297template<typename Archive>
1298void Y8950::Channel::serialize(Archive& ar, unsigned /*version*/)
1299{
1300 ar.serialize("mod", slot[MOD],
1301 "car", slot[CAR],
1302 "freq", freq,
1303 "alg", alg);
1304
1305 if constexpr (Archive::IS_LOADER) {
1306 slot[MOD].updateAll(freq);
1307 slot[CAR].updateAll(freq);
1308 }
1309}
1310
1311template<typename Archive>
1312void Y8950::serialize(Archive& ar, unsigned /*version*/)
1313{
1314 ar.serialize("keyboardConnector", connector,
1315 "adpcm", adpcm,
1316 "timer1", *timer1,
1317 "timer2", *timer2,
1318 "irq", irq);
1319 ar.serialize_blob("registers", reg);
1320 ar.serialize("pm_phase", pm_phase,
1321 "am_phase", am_phase,
1322 "noise_seed", noise_seed,
1323 "noiseA_phase", noiseA_phase,
1324 "noiseB_phase", noiseB_phase,
1325 "noiseA_dphase", noiseA_dPhase,
1326 "noiseB_dphase", noiseB_dPhase,
1327 "channels", ch,
1328 "status", status,
1329 "statusMask", statusMask,
1330 "rythm_mode", rythm_mode,
1331 "am_mode", am_mode,
1332 "pm_mode", pm_mode,
1333 "enabled", enabled);
1334
1335 if constexpr (Archive::IS_LOADER) {
1336 // TODO restore more state from registers
1337 static constexpr std::array<uint8_t, 2> rewriteRegs = {
1338 6, // connector
1339 15, // dac13
1340 };
1341
1342 update_key_status();
1343 EmuTime::param time = motherBoard.getCurrentTime();
1344 for (auto r : rewriteRegs) {
1345 writeReg(r, reg[r], time);
1346 }
1347 }
1348}
1349
1350
1351// SimpleDebuggable
1352
1354 const std::string& name_)
1355 : SimpleDebuggable(motherBoard_, name_ + " regs", "MSX-AUDIO", 0x100)
1356{
1357}
1358
1359uint8_t Y8950::Debuggable::read(unsigned address, EmuTime::param time)
1360{
1361 auto& y8950 = OUTER(Y8950, debuggable);
1362 return y8950.peekReg(narrow<uint8_t>(address), time);
1363}
1364
1365void Y8950::Debuggable::write(unsigned address, uint8_t value, EmuTime::param time)
1366{
1367 auto& y8950 = OUTER(Y8950, debuggable);
1368 y8950.writeReg(narrow<uint8_t>(address), value, time);
1369}
1370
1373
1374} // namespace openmsx
int main()
void writeDAC(int16_t value, EmuTime::param time)
void set()
Set the interrupt request on the bus.
Definition IRQHelper.hh:74
void reset()
Reset the interrupt request on the bus.
Definition IRQHelper.hh:83
EmuTime::param getCurrentTime()
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:90
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:64
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:557
void setStatus(uint8_t flags)
Definition Y8950.cc:1202
uint8_t peekReg(uint8_t rg, EmuTime::param time) const
Definition Y8950.cc:1161
void setEnabled(bool enabled, EmuTime::param time)
Definition Y8950.cc:780
static constexpr int CLOCK_FREQ_DIV
Definition Y8950.hh:29
static constexpr int R04_IRQ_RESET
Definition Y8950.hh:47
uint8_t peekStatus(EmuTime::param time) const
Definition Y8950.cc:1191
uint8_t readStatus(EmuTime::param time) const
Definition Y8950.cc:1184
FixedPoint< EG_DP_BITS - EG_BITS > EnvPhaseIndex
Definition Y8950.hh:107
static constexpr int R04_ST2
Definition Y8950.hh:35
void serialize(Archive &ar, unsigned version)
Definition Y8950.cc:1312
void clearRam()
Definition Y8950.cc:551
static constexpr int R04_ST1
Definition Y8950.hh:33
void writeReg(uint8_t rg, uint8_t data, EmuTime::param time)
Definition Y8950.cc:903
static constexpr int CLOCK_FREQ
Definition Y8950.hh:28
static constexpr int EG_BITS
Definition Y8950.hh:103
void resetStatus(uint8_t flags)
Definition Y8950.cc:1210
uint8_t readReg(uint8_t rg, EmuTime::param time)
Definition Y8950.cc:1146
uint8_t peekRawStatus() const
Definition Y8950.cc:1218
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
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:9
constexpr void fill(ForwardRange &&range, const T &value)
Definition ranges.hh:305
#define OUTER(type, member)
Definition outer.hh:41
#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