openMSX
YMF278.cc
Go to the documentation of this file.
1// Based on ymf278b.c written by R. Belmont and O. Galibert
2
3// Improved by Valley Bell, 2018
4// Thanks to niekniek and l_oliveira for providing recordings from OPL4 hardware.
5// Thanks to superctr and wouterv for discussing changes.
6//
7// Improvements:
8// - added TL interpolation, recordings show that internal TL levels are 0x00..0xff
9// - fixed ADSR speeds, attack rate 15 is now instant
10// - correct clamping of intermediate Rate Correction values
11// - emulation of "loop glitch" (going out-of-bounds by playing a sample faster than it the loop is long)
12// - made calculation of sample position cleaner and closer to how the HW works
13// - increased output resolution from TL (0.375dB) to envelope (0.09375dB)
14// - fixed volume table -6dB steps are done using bit shifts, steps in between are multiplicators
15// - made octave -8 freeze the sample
16// - verified that TL and envelope levels are applied separately, both go silent at -60dB
17// - implemented pseudo-reverb and damping according to manual
18// - made pseudo-reverb ignore Rate Correction (real hardware ignores it)
19// - reimplemented LFO, speed exactly matches the formulas that were probably used when creating the manual
20// - fixed LFO (tremolo) amplitude modulation
21// - made LFO vibrato and tremolo accurate to hardware
22//
23// Known issues:
24// - Octave -8 was only tested with fnum 0. Other fnum values might behave differently.
25
26// This class doesn't model a full YMF278b chip. Instead it only models the
27// wave part. The FM part in modeled in YMF262 (it's almost 100% compatible,
28// the small differences are handled in YMF262). The status register and
29// interaction with the FM registers (e.g. the NEW2 bit) is currently handled
30// in the MSXMoonSound class.
31
32#include "YMF278.hh"
33#include "DeviceConfig.hh"
34#include "MSXMotherBoard.hh"
35#include "MSXException.hh"
36#include "enumerate.hh"
37#include "narrow.hh"
38#include "one_of.hh"
39#include "outer.hh"
40#include "ranges.hh"
41#include "serialize.hh"
42#include "xrange.hh"
43#include <algorithm>
44
45namespace openmsx {
46
47// envelope output entries
48// fixed to match recordings from actual OPL4 -Valley Bell
49static constexpr int MAX_ATT_INDEX = 0x280; // makes attack phase right and also goes well with "envelope stops at -60dB"
50static constexpr int MIN_ATT_INDEX = 0;
51static constexpr int TL_SHIFT = 2; // envelope values are 4x as fine as TL levels
52
53static constexpr unsigned LFO_SHIFT = 18; // LFO period of up to 0x40000 sample
54static constexpr unsigned LFO_PERIOD = 1 << LFO_SHIFT;
55
56// Envelope Generator phases
57static constexpr int EG_ATT = 4;
58static constexpr int EG_DEC = 3;
59static constexpr int EG_SUS = 2;
60static constexpr int EG_REL = 1;
61static constexpr int EG_OFF = 0;
62// these 2 are only used in old savestates (and are converted to EG_REL on load)
63static constexpr int EG_REV = 5; // pseudo reverb
64static constexpr int EG_DMP = 6; // damp
65
66// Pan values, units are -3dB, i.e. 8.
67static constexpr std::array<uint8_t, 16> pan_left = {
68 0, 8, 16, 24, 32, 40, 48, 255, 255, 0, 0, 0, 0, 0, 0, 0
69};
70static constexpr std::array<uint8_t, 16> pan_right = {
71 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 48, 40, 32, 24, 16, 8
72};
73
74// decay level table (3dB per step)
75// 0 - 15: 0, 3, 6, 9,12,15,18,21,24,27,30,33,36,39,42,93 (dB)
76static constexpr int16_t SC(int dB) { return int16_t(dB / 3 * 0x20); }
77static constexpr std::array<int16_t, 16> dl_tab = {
78 SC( 0), SC( 3), SC( 6), SC( 9), SC(12), SC(15), SC(18), SC(21),
79 SC(24), SC(27), SC(30), SC(33), SC(36), SC(39), SC(42), SC(93)
80};
81
82static constexpr uint8_t RATE_STEPS = 8;
83static constexpr std::array<uint8_t, 15 * RATE_STEPS> eg_inc = {
84//cycle:0 1 2 3 4 5 6 7
85 0, 1, 0, 1, 0, 1, 0, 1, // 0 rates 00..12 0 (increment by 0 or 1)
86 0, 1, 0, 1, 1, 1, 0, 1, // 1 rates 00..12 1
87 0, 1, 1, 1, 0, 1, 1, 1, // 2 rates 00..12 2
88 0, 1, 1, 1, 1, 1, 1, 1, // 3 rates 00..12 3
89
90 1, 1, 1, 1, 1, 1, 1, 1, // 4 rate 13 0 (increment by 1)
91 1, 1, 1, 2, 1, 1, 1, 2, // 5 rate 13 1
92 1, 2, 1, 2, 1, 2, 1, 2, // 6 rate 13 2
93 1, 2, 2, 2, 1, 2, 2, 2, // 7 rate 13 3
94
95 2, 2, 2, 2, 2, 2, 2, 2, // 8 rate 14 0 (increment by 2)
96 2, 2, 2, 4, 2, 2, 2, 4, // 9 rate 14 1
97 2, 4, 2, 4, 2, 4, 2, 4, // 10 rate 14 2
98 2, 4, 4, 4, 2, 4, 4, 4, // 11 rate 14 3
99
100 4, 4, 4, 4, 4, 4, 4, 4, // 12 rates 15 0, 15 1, 15 2, 15 3 for decay
101 8, 8, 8, 8, 8, 8, 8, 8, // 13 rates 15 0, 15 1, 15 2, 15 3 for attack (zero time)
102 0, 0, 0, 0, 0, 0, 0, 0, // 14 infinity rates for attack and decay(s)
103};
104
105[[nodiscard]] static constexpr uint8_t O(int a) { return a * RATE_STEPS; }
106static constexpr std::array<uint8_t, 64> eg_rate_select = {
107 O(14),O(14),O(14),O(14), // inf rate
108 O( 0),O( 1),O( 2),O( 3),
109 O( 0),O( 1),O( 2),O( 3),
110 O( 0),O( 1),O( 2),O( 3),
111 O( 0),O( 1),O( 2),O( 3),
112 O( 0),O( 1),O( 2),O( 3),
113 O( 0),O( 1),O( 2),O( 3),
114 O( 0),O( 1),O( 2),O( 3),
115 O( 0),O( 1),O( 2),O( 3),
116 O( 0),O( 1),O( 2),O( 3),
117 O( 0),O( 1),O( 2),O( 3),
118 O( 0),O( 1),O( 2),O( 3),
119 O( 0),O( 1),O( 2),O( 3),
120 O( 4),O( 5),O( 6),O( 7),
121 O( 8),O( 9),O(10),O(11),
122 O(12),O(12),O(12),O(12),
123};
124
125// rate 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
126// shift 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0
127// mask 4095, 2047, 1023, 511, 255, 127, 63, 31, 15, 7, 3, 1, 0, 0, 0, 0
128static constexpr std::array<uint8_t, 64> eg_rate_shift = {
129 12, 12, 12, 12,
130 11, 11, 11, 11,
131 10, 10, 10, 10,
132 9, 9, 9, 9,
133 8, 8, 8, 8,
134 7, 7, 7, 7,
135 6, 6, 6, 6,
136 5, 5, 5, 5,
137 4, 4, 4, 4,
138 3, 3, 3, 3,
139 2, 2, 2, 2,
140 1, 1, 1, 1,
141 0, 0, 0, 0,
142 0, 0, 0, 0,
143 0, 0, 0, 0,
144 0, 0, 0, 0,
145};
146
147
148// number of steps the LFO counter advances per sample
149// LFO frequency (Hz) -> LFO counter steps per sample
150[[nodiscard]] static constexpr int L(double a) { return int((LFO_PERIOD * a) / 44100.0 + 0.5); }
151static constexpr std::array<int, 8> lfo_period = {
152 L(0.168), // step: 1, period: 262144 samples
153 L(2.019), // step: 12, period: 21845 samples
154 L(3.196), // step: 19, period: 13797 samples
155 L(4.206), // step: 25, period: 10486 samples
156 L(5.215), // step: 31, period: 8456 samples
157 L(5.888), // step: 35, period: 7490 samples
158 L(6.224), // step: 37, period: 7085 samples
159 L(7.066), // step: 42, period: 6242 samples
160};
161
162
163// formula used by Yamaha docs:
164// vib_depth_cents(x) = (log2(0x400 + x) - 10) * 1200
165static constexpr std::array<int16_t, 8> vib_depth = {
166 0, // 0.000 cents
167 2, // 3.378 cents
168 3, // 5.065 cents
169 4, // 6.750 cents
170 6, // 10.114 cents
171 12, // 20.170 cents
172 24, // 40.106 cents
173 48, // 79.307 cents
174};
175
176
177// formula used by Yamaha docs:
178// am_depth_db(x) = (x-1) / 0x40 * 6.0
179// They use (x-1), because the depth is multiplied with the AM counter, which has a range of 0..0x7F.
180// Thus the maximum attenuation with x=0x80 is (0x7F * 0x80) >> 7 = 0x7F.
181// reversed formula:
182// am_depth(dB) = round(dB / 6.0 * 0x40) + 1
183static constexpr std::array<uint8_t, 8> am_depth = {
184 0x00, // 0.000 dB
185 0x14, // 1.781 dB
186 0x20, // 2.906 dB
187 0x28, // 3.656 dB
188 0x30, // 4.406 dB
189 0x40, // 5.906 dB
190 0x50, // 7.406 dB
191 0x80, // 11.910 dB
192};
193
194
195YMF278::Slot::Slot()
196{
197 reset();
198}
199
200// Sign extend a 4-bit value to int8_t
201// require: x in range [0..15]
202[[nodiscard]] static constexpr int8_t sign_extend_4(int x)
203{
204 return narrow<int8_t>((x ^ 8) - 8);
205}
206
207// Params: oct in [-8 .. +7]
208// fn in [ 0 .. 1023]
209// We want to interpret oct as a signed 4-bit number and calculate
210// ((fn | 1024) + vib) << (5 + sign_extend_4(oct))
211// Though in this formula the shift can go over a negative distance (in that
212// case we should shift in the other direction).
213[[nodiscard]] static constexpr unsigned calcStep(int8_t oct, uint16_t fn, int16_t vib = 0)
214{
215 if (oct == -8) return 0;
216 unsigned t = (fn + 1024 + vib) << (8 + oct); // use '+' iso '|' (generates slightly better code)
217 return t >> 3; // was shifted 3 positions too far
218}
219
220void YMF278::Slot::reset()
221{
222 wave = FN = TLdest = TL = pan = vib = AM = 0;
223 OCT = 0;
224 DL = 0;
225 AR = D1R = D2R = RC = RR = 0;
226 PRVB = keyon = DAMP = false;
227 stepPtr = 0;
228 step = calcStep(OCT, FN);
229 bits = startAddr = loopAddr = endAddr = 0;
230 env_vol = MAX_ATT_INDEX;
231
232 lfo_active = false;
233 lfo_cnt = 0;
234 lfo = 0;
235
236 state = EG_OFF;
237
238 // not strictly needed, but avoid UMR on savestate
239 pos = 0;
240}
241
242int YMF278::Slot::compute_rate(int val) const
243{
244 if (val == 0) {
245 return 0;
246 } else if (val == 15) {
247 return 63;
248 }
249 int res = val * 4;
250 if (RC != 15) {
251 // clamping verified with HW tests -Valley Bell
252 res += 2 * std::clamp(OCT + RC, 0, 15);
253 res += (FN & 0x200) ? 1 : 0;
254 }
255 return std::clamp(res, 0, 63);
256}
257
258int YMF278::Slot::compute_decay_rate(int val) const
259{
260 if (DAMP) {
261 // damping
262 // The manual lists these values for time and attenuation: (44100 samples/second)
263 // -12dB at 5.8ms, sample 256
264 // -48dB at 8.0ms, sample 352
265 // -72dB at 9.4ms, sample 416
266 // -96dB at 10.9ms, sample 480
267 // This results in these durations and rate values for the respective phases:
268 // 0dB .. -12dB: 256 samples (5.80ms) -> 128 samples per -6dB = rate 48
269 // -12dB .. -48dB: 96 samples (2.18ms) -> 16 samples per -6dB = rate 63
270 // -48dB .. -72dB: 64 samples (1.45ms) -> 16 samples per -6dB = rate 63
271 // -72dB .. -96dB: 64 samples (1.45ms) -> 16 samples per -6dB = rate 63
272 // Damping was verified to ignore rate correction.
273 if (env_vol < dl_tab[4]) {
274 return 48; // 0dB .. -12dB
275 } else {
276 return 63; // -12dB .. -96dB
277 }
278 }
279 if (PRVB) {
280 // pseudo reverb
281 // activated when reaching -18dB, overrides D1R/D2R/RR with reverb rate 5
282 //
283 // The manual is actually a bit unclear and just says "RATE=5",
284 // referring to the D1R/D2R/RR register value. However, later
285 // pages use "RATE" to refer to the "internal" rate, which is
286 // (register * 4) + rate correction. HW recordings prove that
287 // Rate Correction is ignored, so pseudo reverb just sets the
288 // "internal" rate to a value of 4*5 = 20.
289 if (env_vol >= dl_tab[6]) {
290 return 20;
291 }
292 }
293 return compute_rate(val);
294}
295
296int16_t YMF278::Slot::compute_vib() const
297{
298 // verified via hardware recording:
299 // With LFO speed 0 (period 262144 samples), each vibrato step takes
300 // 4096 samples.
301 // -> 64 steps total
302 // Also, with vibrato depth 7 (80 cents) and an F-Num of 0x400, the
303 // final F-Nums are: 0x400 .. 0x43C, 0x43C .. 0x400, 0x400 .. 0x3C4,
304 // 0x3C4 .. 0x400
305 auto lfo_fm = narrow<int16_t>(lfo_cnt / (LFO_PERIOD / 0x40));
306 // results in +0x00..+0x0F, +0x0F..+0x00, -0x00..-0x0F, -0x0F..-0x00
307 if (lfo_fm & 0x10) lfo_fm ^= 0x1F;
308 if (lfo_fm & 0x20) lfo_fm = narrow<int16_t>(-(lfo_fm & 0x0F));
309
310 return narrow<int16_t>((lfo_fm * vib_depth[vib]) / 12);
311}
312
313uint16_t YMF278::Slot::compute_am() const
314{
315 // verified via hardware recording:
316 // With LFO speed 0 (period 262144 samples), each tremolo step takes
317 // 1024 samples.
318 // -> 256 steps total
319 uint16_t lfo_am = lfo_cnt / (LFO_PERIOD / 0x100);
320 // results in 0x00..0x7F, 0x7F..0x00
321 if (lfo_am >= 0x80) lfo_am ^= 0xFF;
322
323 return (lfo_am * am_depth[AM]) >> 7;
324}
325
326
327void YMF278::advance()
328{
329 eg_cnt++;
330
331 // modulo counters for volume interpolation
332 auto tl_int_cnt = eg_cnt % 9; // 0 .. 8
333 auto tl_int_step = (eg_cnt / 9) % 3; // 0 .. 2
334
335 for (auto& op : slots) {
336 // volume interpolation
337 if (tl_int_cnt == 0) {
338 if (tl_int_step == 0) {
339 // decrease volume by one step every 27 samples
340 if (op.TL < op.TLdest) ++op.TL;
341 } else {
342 // increase volume by one step every 13.5 samples
343 if (op.TL > op.TLdest) --op.TL;
344 }
345 }
346
347 if (op.lfo_active) {
348 op.lfo_cnt = (op.lfo_cnt + lfo_period[op.lfo]) & (LFO_PERIOD - 1);
349 }
350
351 // Envelope Generator
352 switch (op.state) {
353 case EG_ATT: { // attack phase
354 uint8_t rate = op.compute_rate(op.AR);
355 // Verified by HW recording (and matches Nemesis' tests of the YM2612):
356 // AR = 0xF during KeyOn results in instant switch to EG_DEC. (see keyOnHelper)
357 // Setting AR = 0xF while the attack phase is in progress freezes the envelope.
358 if (rate >= 63) {
359 break;
360 }
361 uint8_t shift = eg_rate_shift[rate];
362 if (!(eg_cnt & ((1 << shift) - 1))) {
363 uint8_t select = eg_rate_select[rate];
364 // >>4 makes the attack phase's shape match the actual chip -Valley Bell
365 op.env_vol = narrow<int16_t>(op.env_vol + ((~op.env_vol * eg_inc[select + ((eg_cnt >> shift) & 7)]) >> 4));
366 if (op.env_vol <= MIN_ATT_INDEX) {
367 op.env_vol = MIN_ATT_INDEX;
368 // TODO does the real HW skip EG_DEC completely,
369 // or is it active for 1 sample?
370 op.state = op.DL ? EG_DEC : EG_SUS;
371 }
372 }
373 break;
374 }
375 case EG_DEC: { // decay phase
376 uint8_t rate = op.compute_decay_rate(op.D1R);
377 uint8_t shift = eg_rate_shift[rate];
378 if (!(eg_cnt & ((1 << shift) - 1))) {
379 uint8_t select = eg_rate_select[rate];
380 op.env_vol = narrow<int16_t>(op.env_vol + eg_inc[select + ((eg_cnt >> shift) & 7)]);
381 if (op.env_vol >= op.DL) {
382 op.state = (op.env_vol < MAX_ATT_INDEX) ? EG_SUS : EG_OFF;
383 }
384 }
385 break;
386 }
387 case EG_SUS: { // sustain phase
388 uint8_t rate = op.compute_decay_rate(op.D2R);
389 uint8_t shift = eg_rate_shift[rate];
390 if (!(eg_cnt & ((1 << shift) - 1))) {
391 uint8_t select = eg_rate_select[rate];
392 op.env_vol = narrow<int16_t>(op.env_vol + eg_inc[select + ((eg_cnt >> shift) & 7)]);
393 if (op.env_vol >= MAX_ATT_INDEX) {
394 op.env_vol = MAX_ATT_INDEX;
395 op.state = EG_OFF;
396 }
397 }
398 break;
399 }
400 case EG_REL: { // release phase
401 uint8_t rate = op.compute_decay_rate(op.RR);
402 uint8_t shift = eg_rate_shift[rate];
403 if (!(eg_cnt & ((1 << shift) - 1))) {
404 uint8_t select = eg_rate_select[rate];
405 op.env_vol = narrow<int16_t>(op.env_vol + eg_inc[select + ((eg_cnt >> shift) & 7)]);
406 if (op.env_vol >= MAX_ATT_INDEX) {
407 op.env_vol = MAX_ATT_INDEX;
408 op.state = EG_OFF;
409 }
410 }
411 break;
412 }
413 case EG_OFF:
414 // nothing
415 break;
416
417 default:
419 }
420 }
421}
422
423int16_t YMF278::getSample(Slot& slot, uint16_t pos) const
424{
425 // TODO How does this behave when R#2 bit 0 = 1?
426 // As-if read returns 0xff? (Like for CPU memory reads.) Or is
427 // sound generation blocked at some higher level?
428 switch (slot.bits) {
429 case 0: {
430 // 8 bit
431 return narrow_cast<int16_t>(readMem(slot.startAddr + pos) << 8);
432 }
433 case 1: {
434 // 12 bit
435 unsigned addr = slot.startAddr + ((pos / 2) * 3);
436 if (pos & 1) {
437 return narrow_cast<int16_t>(
438 (readMem(addr + 2) << 8) |
439 (readMem(addr + 1) & 0xF0));
440 } else {
441 return narrow_cast<int16_t>(
442 (readMem(addr + 0) << 8) |
443 ((readMem(addr + 1) << 4) & 0xF0));
444 }
445 }
446 case 2: {
447 // 16 bit
448 unsigned addr = slot.startAddr + (pos * 2);
449 return narrow_cast<int16_t>(
450 (readMem(addr + 0) << 8) |
451 (readMem(addr + 1) << 0));
452 }
453 default:
454 // TODO unspecified
455 return 0;
456 }
457}
458
459uint16_t YMF278::nextPos(Slot& slot, uint16_t pos, uint16_t increment)
460{
461 // If there is a 4-sample loop and you advance 12 samples per step,
462 // it may exceed the end offset.
463 // This is abused by the "Lizard Star" song to generate noise at 0:52. -Valley Bell
464 pos += increment;
465 if ((uint32_t(pos) + slot.endAddr) >= 0x10000) // check position >= (negated) end address
466 pos += slot.endAddr + slot.loopAddr; // This is how the actual chip does it.
467 return pos;
468}
469
470bool YMF278::anyActive()
471{
472 return ranges::any_of(slots, [](auto& op) { return op.state != EG_OFF; });
473}
474
475// In: 'envVol', 0=max volume, others -> -3/32 = -0.09375 dB/step
476// Out: 'x' attenuated by the corresponding factor.
477// Note: microbenchmarks have shown that re-doing this calculation is about the
478// same speed as using a 4kB lookup table.
479static constexpr int vol_factor(int x, unsigned envVol)
480{
481 if (envVol >= MAX_ATT_INDEX) return 0; // hardware clips to silence below -60dB
482 int vol_mul = 0x80 - narrow<int>(envVol & 0x3F); // 0x40 values per 6dB
483 int vol_shift = 7 + narrow<int>(envVol >> 6);
484 return (x * ((0x8000 * vol_mul) >> vol_shift)) >> 15;
485}
486
487void YMF278::setMixLevel(uint8_t x, EmuTime::param time)
488{
489 static constexpr std::array<float, 8> level = {
490 (1.00f / 1), // 0dB
491 (0.75f / 1), // -3dB (approx)
492 (1.00f / 2), // -6dB
493 (0.75f / 2), // -9dB (approx)
494 (1.00f / 4), // -12dB
495 (0.75f / 4), // -15dB (approx)
496 (1.00f / 8), // -18dB
497 (0.00f ), // -inf dB
498 };
499 setSoftwareVolume(level[x & 7], level[(x >> 3) & 7], time);
500}
501
502void YMF278::generateChannels(std::span<float*> bufs, unsigned num)
503{
504 if (!anyActive()) {
505 // TODO update internal state, even if muted
506 // TODO also mute individual channels
507 ranges::fill(bufs, nullptr);
508 return;
509 }
510
511 for (auto j : xrange(num)) {
512 for (auto i : xrange(24)) {
513 auto& sl = slots[i];
514 if (sl.state == EG_OFF) {
515 //bufs[i][2 * j + 0] += 0;
516 //bufs[i][2 * j + 1] += 0;
517 continue;
518 }
519
520 auto sample = narrow_cast<int16_t>(
521 (getSample(sl, sl.pos) * (0x10000 - sl.stepPtr) +
522 getSample(sl, nextPos(sl, sl.pos, 1)) * sl.stepPtr) >> 16);
523 // TL levels are 00..FF internally (TL register value 7F is mapped to TL level FF)
524 // Envelope levels have 4x the resolution (000..3FF)
525 // Volume levels are approximate logarithmic. -6dB result in half volume. Steps in between use linear interpolation.
526 // A volume of -60dB or lower results in silence. (value 0x280..0x3FF).
527 // Recordings from actual hardware indicate that TL level and envelope level are applied separately.
528 // Each of them is clipped to silence below -60dB, but TL+envelope might result in a lower volume. -Valley Bell
529 uint16_t envVol = std::min(sl.env_vol + ((sl.lfo_active && sl.AM) ? sl.compute_am() : 0),
530 MAX_ATT_INDEX);
531 int smplOut = vol_factor(vol_factor(sample, envVol), sl.TL << TL_SHIFT);
532
533 // Panning is also done separately. (low-volume TL + low-volume panning goes below -60dB)
534 // I'll be taking wild guess and assume that -3dB is approximated with 75%. (same as with TL and envelope levels)
535 // The same applies to the PCM mix level.
536 int32_t volLeft = pan_left [sl.pan]; // note: register 0xF9 is handled externally
537 int32_t volRight = pan_right[sl.pan];
538 // 0 -> 0x20, 8 -> 0x18, 16 -> 0x10, 24 -> 0x0C, etc. (not using vol_factor here saves array boundary checks)
539 volLeft = (0x20 - (volLeft & 0x0f)) >> (volLeft >> 4);
540 volRight = (0x20 - (volRight & 0x0f)) >> (volRight >> 4);
541
542 bufs[i][2 * j + 0] += narrow_cast<float>((smplOut * volLeft ) >> 5);
543 bufs[i][2 * j + 1] += narrow_cast<float>((smplOut * volRight) >> 5);
544
545 unsigned step = (sl.lfo_active && sl.vib)
546 ? calcStep(sl.OCT, sl.FN, sl.compute_vib())
547 : sl.step;
548 sl.stepPtr += step;
549
550 if (sl.stepPtr >= 0x10000) {
551 sl.pos = nextPos(sl, sl.pos, sl.stepPtr >> 16);
552 sl.stepPtr &= 0xffff;
553 }
554 }
555 advance();
556 }
557}
558
559void YMF278::keyOnHelper(YMF278::Slot& slot)
560{
561 // Unlike FM, the envelope level is reset. (And it makes sense, because you restart the sample.)
562 slot.env_vol = MAX_ATT_INDEX;
563 if (slot.compute_rate(slot.AR) < 63) {
564 slot.state = EG_ATT;
565 } else {
566 // Nuke.YKT verified that the FM part does it exactly this way,
567 // and the OPL4 manual says it's instant as well.
568 slot.env_vol = MIN_ATT_INDEX;
569 // see comment in 'case EG_ATT' in YMF278::advance()
570 slot.state = slot.DL ? EG_DEC : EG_SUS;
571 }
572 slot.stepPtr = 0;
573 slot.pos = 0;
574}
575
576void YMF278::writeReg(uint8_t reg, uint8_t data, EmuTime::param time)
577{
578 updateStream(time); // TODO optimize only for regs that directly influence sound
579 writeRegDirect(reg, data, time);
580}
581
582void YMF278::writeRegDirect(uint8_t reg, uint8_t data, EmuTime::param time)
583{
584 // Handle slot registers specifically
585 if (reg >= 0x08 && reg <= 0xF7) {
586 int sNum = (reg - 8) % 24;
587 auto& slot = slots[sNum];
588 switch ((reg - 8) / 24) {
589 case 0: {
590 slot.wave = (slot.wave & 0x100) | data;
591 int waveTblHdr = (regs[2] >> 2) & 0x7;
592 int base = (slot.wave < 384 || !waveTblHdr) ?
593 (slot.wave * 12) :
594 (waveTblHdr * 0x80000 + ((slot.wave - 384) * 12));
595 std::array<uint8_t, 12> buf;
596 for (auto i : xrange(12)) {
597 // TODO What if R#2 bit 0 = 1?
598 // See also getSample()
599 buf[i] = readMem(base + i);
600 }
601 slot.bits = (buf[0] & 0xC0) >> 6;
602 slot.startAddr = buf[2] | (buf[1] << 8) | ((buf[0] & 0x3F) << 16);
603 slot.loopAddr = buf[4] | (buf[3] << 8);
604 slot.endAddr = buf[6] | (buf[5] << 8);
605 for (auto i : xrange(7, 12)) {
606 // Verified on real YMF278:
607 // After tone loading, if you read these
608 // registers, their value actually has changed.
609 writeRegDirect(8 + sNum + (i - 2) * 24, buf[i], time);
610 }
611 if (slot.keyon) {
612 keyOnHelper(slot);
613 } else {
614 slot.stepPtr = 0;
615 slot.pos = 0;
616 }
617 break;
618 }
619 case 1: {
620 slot.wave = (slot.wave & 0xFF) | ((data & 0x1) << 8);
621 slot.FN = (slot.FN & 0x380) | (data >> 1);
622 slot.step = calcStep(slot.OCT, slot.FN);
623 break;
624 }
625 case 2: {
626 slot.FN = (slot.FN & 0x07F) | ((data & 0x07) << 7);
627 slot.PRVB = (data & 0x08) != 0;
628 slot.OCT = sign_extend_4((data & 0xF0) >> 4);
629 slot.step = calcStep(slot.OCT, slot.FN);
630 break;
631 }
632 case 3: {
633 uint8_t t = data >> 1;
634 slot.TLdest = (t != 0x7f) ? t : 0xff; // verified on HW via volume interpolation
635 if (data & 1) {
636 // directly change volume
637 slot.TL = slot.TLdest;
638 } else {
639 // interpolate volume
640 }
641 break;
642 }
643 case 4:
644 if (data & 0x10) {
645 // output to DO1 pin:
646 // this pin is not used in MoonSound
647 // we emulate this by muting the sound
648 slot.pan = 8; // both left/right -inf dB
649 } else {
650 slot.pan = data & 0x0F;
651 }
652
653 if (data & 0x20) {
654 // LFO reset
655 slot.lfo_active = false;
656 slot.lfo_cnt = 0;
657 } else {
658 // LFO activate
659 slot.lfo_active = true;
660 }
661
662 slot.DAMP = (data & 0x40) != 0;
663
664 if (data & 0x80) {
665 if (!slot.keyon) {
666 slot.keyon = true;
667 keyOnHelper(slot);
668 }
669 } else {
670 if (slot.keyon) {
671 slot.keyon = false;
672 slot.state = EG_REL;
673 }
674 }
675 break;
676 case 5:
677 slot.lfo = (data >> 3) & 0x7;
678 slot.vib = data & 0x7;
679 break;
680 case 6:
681 slot.AR = data >> 4;
682 slot.D1R = data & 0xF;
683 break;
684 case 7:
685 slot.DL = dl_tab[data >> 4];
686 slot.D2R = data & 0xF;
687 break;
688 case 8:
689 slot.RC = data >> 4;
690 slot.RR = data & 0xF;
691 break;
692 case 9:
693 slot.AM = data & 0x7;
694 break;
695 }
696 } else {
697 // All non-slot registers
698 switch (reg) {
699 case 0x00: // TEST
700 case 0x01:
701 break;
702
703 case 0x02:
704 // wave-table-header / memory-type / memory-access-mode
705 // Simply store in regs[2]
706 break;
707
708 case 0x03:
709 // Verified on real YMF278:
710 // * Don't update the 'memAdr' variable on writes to
711 // reg 3 and 4. Only store the value in the 'regs'
712 // array for later use.
713 // * The upper 2 bits are not used to address the
714 // external memories (so from a HW pov they don't
715 // matter). But if you read back this register, the
716 // upper 2 bits always read as '0' (even if you wrote
717 // '1'). So we mask the bits here already.
718 data &= 0x3F;
719 break;
720
721 case 0x04:
722 // See reg 3.
723 break;
724
725 case 0x05:
726 // Verified on real YMF278: (see above)
727 // Only writes to reg 5 change the (full) 'memAdr'.
728 memAdr = (regs[3] << 16) | (regs[4] << 8) | data;
729 break;
730
731 case 0x06: // memory data
732 if (regs[2] & 1) {
733 writeMem(memAdr, data);
734 ++memAdr; // no need to mask (again) here
735 } else {
736 // Verified on real YMF278:
737 // - writes are ignored
738 // - memAdr is NOT increased
739 }
740 break;
741
742 case 0xf8: // These are implemented in MSXMoonSound.cc
743 case 0xf9:
744 break;
745 }
746 }
747
748 regs[reg] = data;
749}
750
751uint8_t YMF278::readReg(uint8_t reg)
752{
753 // no need to call updateStream(time)
754 uint8_t result = peekReg(reg);
755 if (reg == 6) {
756 // Memory Data Register
757 if (regs[2] & 1) {
758 // Verified on real YMF278:
759 // memAdr is only increased when 'regs[2] & 1'
760 ++memAdr; // no need to mask (again) here
761 }
762 }
763 return result;
764}
765
766uint8_t YMF278::peekReg(uint8_t reg) const
767{
768 switch (reg) {
769 case 2: // 3 upper bits are device ID
770 return (regs[2] & 0x1F) | 0x20;
771
772 case 6: // Memory Data Register
773 if (regs[2] & 1) {
774 return readMem(memAdr);
775 } else {
776 // Verified on real YMF278
777 return 0xff;
778 }
779
780 default:
781 return regs[reg];
782 }
783}
784
785static constexpr unsigned INPUT_RATE = 44100;
786
787static size_t getRamSize(int ramSizeInKb)
788{
789 if ((ramSizeInKb != 0) && // - -
790 (ramSizeInKb != 128) && // 128kB -
791 (ramSizeInKb != 256) && // 128kB 128kB
792 (ramSizeInKb != 512) && // 512kB -
793 (ramSizeInKb != 640) && // 512kB 128kB
794 (ramSizeInKb != 1024) && // 512kB 512kB
795 (ramSizeInKb != 2048)) { // 512kB 512kB 512kB 512kB
796 throw MSXException(
797 "Wrong sample ram size for MoonSound (YMF278). "
798 "Got ", ramSizeInKb, ", but must be one of "
799 "0, 128, 256, 512, 640, 1024 or 2048.");
800 }
801 return size_t(ramSizeInKb) * 1024; // kilo-bytes -> bytes
802}
803
804YMF278::YMF278(const std::string& name_, int ramSizeInKb,
805 const DeviceConfig& config)
806 : ResampledSoundDevice(config.getMotherBoard(), name_, "MoonSound wave-part",
807 24, INPUT_RATE, true)
808 , motherBoard(config.getMotherBoard())
809 , debugRegisters(motherBoard, getName())
810 , debugMemory (motherBoard, getName())
811 , rom(getName() + " ROM", "rom", config)
812 , ram(config, getName() + " RAM", "YMF278 sample RAM",
813 getRamSize(ramSizeInKb)) // check size before allocating
814{
815 if (rom.size() != 0x200000) { // 2MB
816 throw MSXException(
817 "Wrong ROM for MoonSound (YMF278). The ROM (usually "
818 "called yrw801.rom) should have a size of exactly 2MB.");
819 }
820
821 memAdr = 0; // avoid UMR
822 ranges::fill(regs, 0);
823
824 registerSound(config);
825 reset(motherBoard.getCurrentTime()); // must come after registerSound() because of call to setSoftwareVolume() via setMixLevel()
826}
827
829{
831}
832
834{
835 ram.clear(0);
836}
837
838void YMF278::reset(EmuTime::param time)
839{
840 updateStream(time);
841
842 eg_cnt = 0;
843
844 for (auto& op : slots) {
845 op.reset();
846 }
847 regs[2] = 0; // avoid UMR
848 for (int i = 0xf7; i >= 0; --i) { // reverse order to avoid UMR
849 writeRegDirect(i, 0, time);
850 }
851 memAdr = 0;
852 setMixLevel(0, time);
853}
854
855// This routine translates an address from the (upper) MoonSound address space
856// to an address inside the (linearized) SRAM address space.
857//
858// The following info is based on measurements on a real MoonSound (v2.0)
859// PCB. This PCB can have several possible SRAM configurations:
860// 128kB:
861// 1 SRAM chip of 128kB, chip enable (/CE) of this SRAM chip is connected to
862// the 1Y0 output of a 74LS139 (2-to-4 decoder). The enable input of the
863// 74LS139 is connected to YMF278 pin /MCS6 and the 74LS139 1B:1A inputs are
864// connected to YMF278 pins MA18:MA17. So the SRAM is selected when /MC6 is
865// active and MA18:MA17 == 0:0.
866// 256kB:
867// 2 SRAM chips of 128kB. First one connected as above. Second one has /CE
868// connected to 74LS139 pin 1Y1. So SRAM2 is selected when /MSC6 is active
869// and MA18:MA17 == 0:1.
870// 512kB:
871// 1 SRAM chip of 512kB, /CE connected to /MCS6
872// 640kB:
873// 1 SRAM chip of 512kB, /CE connected to /MCS6
874// 1 SRAM chip of 128kB, /CE connected to /MCS7.
875// (This means SRAM2 is potentially mirrored over a 512kB region)
876// 1024kB:
877// 1 SRAM chip of 512kB, /CE connected to /MCS6
878// 1 SRAM chip of 512kB, /CE connected to /MCS7
879// 2048kB:
880// 1 SRAM chip of 512kB, /CE connected to /MCS6
881// 1 SRAM chip of 512kB, /CE connected to /MCS7
882// 1 SRAM chip of 512kB, /CE connected to /MCS8
883// 1 SRAM chip of 512kB, /CE connected to /MCS9
884// This configuration is not so easy to create on the v2.0 PCB. So it's
885// very rare.
886//
887// So the /MCS6 and /MCS7 (and /MCS8 and /MCS9 in case of 2048kB) signals are
888// used to select the different SRAM chips. The meaning of these signals
889// depends on the 'memory access mode'. This mode can be changed at run-time
890// via bit 1 in register 2. The following table indicates for which regions
891// these signals are active (normally MoonSound should be used with mode=0):
892// mode=0 mode=1
893// /MCS6 0x200000-0x27FFFF 0x380000-0x39FFFF
894// /MCS7 0x280000-0x2FFFFF 0x3A0000-0x3BFFFF
895// /MCS8 0x300000-0x37FFFF 0x3C0000-0x3DFFFF
896// /MCS9 0x380000-0x3FFFFF 0x3E0000-0x3FFFFF
897//
898// (For completeness) MoonSound also has 2MB ROM (YRW801), /CE of this ROM is
899// connected to YMF278 /MCS0. In both mode=0 and mode=1 this signal is active
900// for the region 0x000000-0x1FFFFF. (But this routine does not handle ROM).
901unsigned YMF278::getRamAddress(unsigned addr) const
902{
903 addr -= 0x200000; // RAM starts at 0x200000
904 if (regs[2] & 2) [[unlikely]] {
905 // Normally MoonSound is used in 'memory access mode = 0'. But
906 // in the rare case that mode=1 we adjust the address.
907 if ((0x180000 <= addr) && (addr <= 0x1FFFFF)) {
908 addr -= 0x180000;
909 switch (addr & 0x060000) {
910 case 0x000000: // [0x380000-0x39FFFF]
911 // 1st 128kB of SRAM1
912 break;
913 case 0x020000: // [0x3A0000-0x3BFFFF]
914 if (ram.size() == 256 * 1024) {
915 // 2nd 128kB SRAM chip
916 } else {
917 // 2nd block of 128kB in SRAM2
918 // In case of 512+128, we use mirroring
919 addr += 0x080000;
920 }
921 break;
922 case 0x040000: // [0x3C0000-0x3DFFFF]
923 // 3rd 128kB block in SRAM3
924 addr += 0x100000;
925 break;
926 case 0x060000: // [0x3EFFFF-0x3FFFFF]
927 // 4th 128kB block in SRAM4
928 addr += 0x180000;
929 break;
930 }
931 } else {
932 addr = unsigned(-1); // unmapped
933 }
934 }
935 if (ram.size() == 640 * 1024) {
936 // Verified on real MoonSound cartridge (v2.0): In case of
937 // 640kB (1x512kB + 1x128kB), the 128kB SRAM chip is 4 times
938 // visible. None of the other SRAM configurations show similar
939 // mirroring (because the others are powers of two).
940 if (addr > 0x080000) {
941 addr &= ~0x060000;
942 }
943 }
944 return addr;
945}
946
947uint8_t YMF278::readMem(unsigned address) const
948{
949 // Verified on real YMF278: address space wraps at 4MB.
950 address &= 0x3FFFFF;
951 if (address < 0x200000) {
952 // ROM connected to /MCS0
953 return rom[address];
954 } else {
955 unsigned ramAddr = getRamAddress(address);
956 if (ramAddr < ram.size()) {
957 return ram[ramAddr];
958 } else {
959 // unmapped region
960 return 255; // TODO check
961 }
962 }
963}
964
965void YMF278::writeMem(unsigned address, uint8_t value)
966{
967 address &= 0x3FFFFF;
968 if (address < 0x200000) {
969 // can't write to ROM
970 } else {
971 unsigned ramAddr = getRamAddress(address);
972 if (ramAddr < ram.size()) {
973 ram.write(ramAddr, value);
974 } else {
975 // can't write to unmapped memory
976 }
977 }
978}
979
980// version 1: initial version, some variables were saved as char
981// version 2: serialization framework was fixed to save/load chars as numbers
982// but for backwards compatibility we still load old savestates as
983// characters
984// version 3: 'step' is no longer stored (it is recalculated)
985// version 4:
986// - removed members: 'lfo', 'LD', 'active'
987// - new members 'TLdest', 'keyon', 'DAMP' restored from registers instead of serialized
988// - store 'OCT' sign-extended
989// - store 'endAddr' as 2s complement
990// - removed EG_DMP and EG_REV enum values from 'state'
991// version 5:
992// - re-added 'lfo' member. This is not stored in the savestate, instead it's
993// restored from register values in YMF278::serialize()
994// - removed members 'lfo_step' and ' 'lfo_max'
995// - 'lfo_cnt' has changed meaning (but we don't try to translate old to new meaning)
996// version 6:
997// - removed members: 'sample1', 'sample2'
998template<typename Archive>
999void YMF278::Slot::serialize(Archive& ar, unsigned version)
1000{
1001 // TODO restore more state from registers
1002 ar.serialize("startaddr", startAddr,
1003 "loopaddr", loopAddr,
1004 "stepptr", stepPtr,
1005 "pos", pos,
1006 "env_vol", env_vol,
1007 "lfo_cnt", lfo_cnt,
1008 "DL", DL,
1009 "wave", wave,
1010 "FN", FN);
1011 if (ar.versionAtLeast(version, 4)) {
1012 ar.serialize("endaddr", endAddr,
1013 "OCT", OCT);
1014 } else {
1015 unsigned e = 0; ar.serialize("endaddr", e); endAddr = (e ^ 0xffff) + 1;
1016
1017 char O = 0;
1018 if (ar.versionAtLeast(version, 2)) {
1019 ar.serialize("OCT", O);
1020 } else {
1021 ar.serializeChar("OCT", O);
1022 }
1023 OCT = sign_extend_4(O);
1024 }
1025
1026 if (ar.versionAtLeast(version, 2)) {
1027 ar.serialize("PRVB", PRVB,
1028 "TL", TL,
1029 "pan", pan,
1030 "vib", vib,
1031 "AM", AM,
1032 "AR", AR,
1033 "D1R", D1R,
1034 "D2R", D2R,
1035 "RC", RC,
1036 "RR", RR);
1037 } else {
1038 // for backwards compatibility with old savestates
1039 char PRVB_ = 0; ar.serializeChar("PRVB", PRVB_); PRVB = PRVB_;
1040 char TL_ = 0; ar.serializeChar("TL", TL_ ); TL = TL_;
1041 char pan_ = 0; ar.serializeChar("pan", pan_); pan = pan_;
1042 char vib_ = 0; ar.serializeChar("vib", vib_); vib = vib_;
1043 char AM_ = 0; ar.serializeChar("AM", AM_ ); AM = AM_;
1044 char AR_ = 0; ar.serializeChar("AR", AR_ ); AR = AR_;
1045 char D1R_ = 0; ar.serializeChar("D1R", D1R_); D1R = D1R_;
1046 char D2R_ = 0; ar.serializeChar("D2R", D2R_); D2R = D2R_;
1047 char RC_ = 0; ar.serializeChar("RC", RC_ ); RC = RC_;
1048 char RR_ = 0; ar.serializeChar("RR", RR_ ); RR = RR_;
1049 }
1050 ar.serialize("bits", bits,
1051 "lfo_active", lfo_active);
1052
1053 ar.serialize("state", state);
1054 if (ar.versionBelow(version, 4)) {
1055 assert(Archive::IS_LOADER);
1056 if (state == one_of(EG_REV, EG_DMP)) {
1057 state = EG_REL;
1058 }
1059 }
1060
1061 // Recalculate redundant state
1062 if constexpr (Archive::IS_LOADER) {
1063 step = calcStep(OCT, FN);
1064 }
1065
1066 // This old comment is NOT completely true:
1067 // Older version also had "env_vol_step" and "env_vol_lim" but those
1068 // members were nowhere used, so removed those in the current
1069 // version (it's ok to remove members from the savestate without
1070 // updating the version number).
1071 // When you remove member variables without increasing the version
1072 // number, new openMSX executables can still read old savestates. And
1073 // if you try to load a new savestate in an old openMSX version you do
1074 // get a (cryptic) error message. But if the version number is
1075 // increased the error message is much clearer.
1076}
1077
1078// version 1: initial version
1079// version 2: loadTime and busyTime moved to MSXMoonSound class
1080// version 3: memAdr cannot be restored from register values
1081// version 4: implement ram via Ram class
1082template<typename Archive>
1083void YMF278::serialize(Archive& ar, unsigned version)
1084{
1085 ar.serialize("slots", slots,
1086 "eg_cnt", eg_cnt);
1087 if (ar.versionAtLeast(version, 4)) {
1088 ar.serialize("ram", ram);
1089 } else {
1090 ar.serialize_blob("ram", ram.getWriteBackdoor());
1091 }
1092 ar.serialize_blob("registers", regs);
1093 if (ar.versionAtLeast(version, 3)) { // must come after 'regs'
1094 ar.serialize("memadr", memAdr);
1095 } else {
1096 assert(Archive::IS_LOADER);
1097 // Old formats didn't store 'memAdr' so we also can't magically
1098 // restore the correct value. The best we can do is restore the
1099 // last set address.
1100 regs[3] &= 0x3F; // mask upper two bits
1101 memAdr = (regs[3] << 16) | (regs[4] << 8) | regs[5];
1102 }
1103
1104 // TODO restore more state from registers
1105 if constexpr (Archive::IS_LOADER) {
1106 for (auto [i, sl] : enumerate(slots)) {
1107 auto t = regs[0x50 + i] >> 1;
1108 sl.TLdest = (t != 0x7f) ? t : 0xff;
1109
1110 sl.keyon = (regs[0x68 + i] & 0x80) != 0;
1111 sl.DAMP = (regs[0x68 + i] & 0x40) != 0;
1112 sl.lfo = (regs[0x80 + i] >> 3) & 7;
1113 }
1114 }
1115}
1117
1118
1119// class DebugRegisters
1120
1121YMF278::DebugRegisters::DebugRegisters(MSXMotherBoard& motherBoard_,
1122 const std::string& name_)
1123 : SimpleDebuggable(motherBoard_, name_ + " regs",
1124 "OPL4 registers", 0x100)
1125{
1126}
1127
1128uint8_t YMF278::DebugRegisters::read(unsigned address)
1129{
1130 auto& ymf278 = OUTER(YMF278, debugRegisters);
1131 return ymf278.peekReg(address);
1132}
1133
1134void YMF278::DebugRegisters::write(unsigned address, uint8_t value, EmuTime::param time)
1135{
1136 auto& ymf278 = OUTER(YMF278, debugRegisters);
1137 ymf278.writeReg(address, value, time);
1138}
1139
1140
1141// class DebugMemory
1142
1143YMF278::DebugMemory::DebugMemory(MSXMotherBoard& motherBoard_,
1144 const std::string& name_)
1145 : SimpleDebuggable(motherBoard_, name_ + " mem",
1146 "OPL4 memory (includes both ROM and RAM)", 0x400000) // 4MB
1147{
1148}
1149
1150uint8_t YMF278::DebugMemory::read(unsigned address)
1151{
1152 auto& ymf278 = OUTER(YMF278, debugMemory);
1153 return ymf278.readMem(address);
1154}
1155
1156void YMF278::DebugMemory::write(unsigned address, uint8_t value)
1157{
1158 auto& ymf278 = OUTER(YMF278, debugMemory);
1159 ymf278.writeMem(address, value);
1160}
1161
1162} // namespace openmsx
TclObject t
Definition: one_of.hh:7
EmuTime::param getCurrentTime()
Convenience method: This is the same as getScheduler().getCurrentTime().
auto size() const
Definition: Rom.hh:36
void updateStream(EmuTime::param time)
Definition: SoundDevice.cc:138
void setSoftwareVolume(float volume, EmuTime::param time)
Change the 'software volume' of this sound device.
Definition: SoundDevice.cc:143
void unregisterSound()
Unregisters this sound device with the Mixer.
Definition: SoundDevice.cc:133
void registerSound(const DeviceConfig &config)
Registers this sound device with the Mixer.
Definition: SoundDevice.cc:88
void clear(byte c=0xff)
Definition: TrackedRam.hh:46
void write(size_t addr, byte value)
Definition: TrackedRam.hh:41
std::span< byte > getWriteBackdoor()
Definition: TrackedRam.hh:55
size_t size() const
Definition: TrackedRam.hh:20
uint8_t readReg(uint8_t reg)
Definition: YMF278.cc:751
void writeReg(uint8_t reg, uint8_t data, EmuTime::param time)
Definition: YMF278.cc:576
void setMixLevel(uint8_t x, EmuTime::param time)
Definition: YMF278.cc:487
void reset(EmuTime::param time)
Definition: YMF278.cc:838
void writeMem(unsigned address, uint8_t value)
Definition: YMF278.cc:965
uint8_t readMem(unsigned address) const
Definition: YMF278.cc:947
void clearRam()
Definition: YMF278.cc:833
void serialize(Archive &ar, unsigned version)
Definition: YMF278.cc:1083
uint8_t peekReg(uint8_t reg) const
Definition: YMF278.cc:766
YMF278(const std::string &name, int ramSizeInKb, const DeviceConfig &config)
Definition: YMF278.cc:804
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 e
Definition: Math.hh:20
constexpr vecN< N, T > min(const vecN< N, T > &x, const vecN< N, T > &y)
Definition: gl_vec.hh:266
constexpr vecN< N, T > clamp(const vecN< N, T > &x, const vecN< N, T > &minVal, const vecN< N, T > &maxVal)
Definition: gl_vec.hh:293
std::string getName(KeyCode keyCode)
Translate key code to key name.
Definition: Keys.cc:730
This file implemented 3 utility functions:
Definition: Autofire.cc:9
void serialize(Archive &ar, T &t, unsigned version)
bool any_of(InputRange &&range, UnaryPredicate pred)
Definition: ranges.hh:192
constexpr void fill(ForwardRange &&range, const T &value)
Definition: ranges.hh:287
#define OUTER(type, member)
Definition: outer.hh:41
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:1021
#define UNREACHABLE
Definition: unreachable.hh:38
constexpr auto xrange(T e)
Definition: xrange.hh:133