openMSX
SCC.cc
Go to the documentation of this file.
1 //-----------------------------------------------------------------------------
2 //
3 // On Mon, 24 Feb 2003, Jon De Schrijder wrote:
4 //
5 // I've done some measurements with the scope on the output of the SCC.
6 // I didn't do timing tests, only amplitude checks:
7 //
8 // I know now for sure, the amplitude calculation works as follows:
9 //
10 // AmpOut=640+AmpA+AmpB+AmpC+AmpD+AmpE
11 //
12 // range AmpOut (11 bits positive number=SCC digital output): [+40...+1235]
13 //
14 // AmpA="((SampleValue*VolA) AND #7FF0) div 16"
15 // AmpB="((SampleValue*VolB) AND #7FF0) div 16"
16 // AmpC="((SampleValue*VolC) AND #7FF0) div 16"
17 // AmpD="((SampleValue*VolD) AND #7FF0) div 16"
18 // AmpE="((SampleValue*VolE) AND #7FF0) div 16"
19 //
20 // Setting the enablebit to zero, corresponds with VolX=0.
21 //
22 // SampleValue range [-128...+127]
23 // VolX range [0..15]
24 //
25 // Notes:
26 // * SampleValue*VolX is calculated (signed multiplication) and the lower 4
27 // bits are dropped (both in case the value is positive or negative), before
28 // the addition of the 5 values is done. This was tested by setting
29 // SampleValue=+1 and VolX=15 of different channels. The resulting AmpOut=640,
30 // indicating that the 4 lower bits were dropped *before* the addition.
31 //
32 //-----------------------------------------------------------------------------
33 //
34 // On Mon, 14 Apr 2003, Manuel Pazos wrote
35 //
36 // I have some info about SCC/SCC+ that I hope you find useful. It is about
37 // "Mode Setting Register", also called "Deformation Register" Here it goes:
38 //
39 // bit0: 4 bits frequency (%XXXX00000000). Equivalent to
40 // (normal frequency >> 8) bits0-7 are ignored
41 // bit1: 8 bits frequency (%0000XXXXXXXX) bits8-11 are ignored
42 // bit2:
43 // bit3:
44 // bit4:
45 // bit5: wave data is played from begining when frequency is changed
46 // bit6: rotate all waves data. You can't write to them. Rotation speed
47 // =3.58Mhz / (channel i frequency + 1)
48 // bit7: rotate channel 4 wave data. You can't write to that channel
49 // data.ONLY works in MegaROM SCC (not in SCC+)
50 //
51 // If bit7 and bit6 are set, only channel 1-3 wave data rotates . You can't
52 // write to ANY wave data. And there is a weird behaviour in this setting. It
53 // seems SCC sound is corrupted in anyway with MSX databus or so. Try to
54 // activate them (with proper waves, freqs, and vol.) and execute DIR command
55 // on DOS. You will hear "noise" This seems to be fixed in SCC+
56 //
57 // Reading Mode Setting Register, is equivalent to write #FF to it.
58 //
59 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
60 //
61 // Additions:
62 // - Setting both bit0 and bit1 is equivalent to setting only bit1
63 // - A rotation goes like this:
64 // wavedata[0:31] = wavedata[1:31].wavedata[0]
65 // - Channel 4-5 rotation speed is set by channel 5 freq (channel 4 freq
66 // is ignored for rotation)
67 //
68 // Also see this MRC thread:
69 // http://www.msx.org/forumtopicl7875.html
70 //
71 //-----------------------------------------------------------------------------
72 //
73 // On Sat, 09 Sep 2005, NYYRIKKI wrote (MRC post)
74 //
75 // ...
76 //
77 // One important thing to know is that change of volume is not implemented
78 // immediately in SCC. Normally it is changed when next byte from sample memory
79 // is played, but writing value to frequency causes current byte to be started
80 // again. As in this example we write values very quickly to frequency registers
81 // the internal sample counter does not actually move at all.
82 //
83 // Third method is a variation of first method. As we don't know where SCC is
84 // playing, let's update the whole sample memory with one and same new value.
85 // To make sample rate not variable in low sample rates we first stop SCC from
86 // reading sample memory. This can be done by writing value less than 9 to
87 // frequency. Now we can update sample RAM so, that output does not change.
88 // After sample RAM has been updated, we start SCC internal counter so that
89 // value (where ever the counter was) is sent to output. This routine can be
90 // found below as example 3.
91 //
92 // ...
93 //
94 //
95 //
96 // Something completely different: the SCC+ is actually called SCC-I.
97 //-----------------------------------------------------------------------------
98 
99 #include "SCC.hh"
100 #include "DeviceConfig.hh"
101 #include "cstd.hh"
102 #include "enumerate.hh"
103 #include "likely.hh"
104 #include "outer.hh"
105 #include "ranges.hh"
106 #include "serialize.hh"
107 #include "unreachable.hh"
108 #include "xrange.hh"
109 #include <cmath>
110 
111 namespace openmsx {
112 
113 static constexpr auto INPUT_RATE = unsigned(cstd::round(3579545.0 / 32));
114 
115 static constexpr auto calcDescription(SCC::ChipMode mode)
116 {
117  return (mode == SCC::SCC_Real) ? static_string_view("Konami SCC")
118  : static_string_view("Konami SCC+");
119 }
120 
121 SCC::SCC(const std::string& name_, const DeviceConfig& config,
122  EmuTime::param time, ChipMode mode)
124  config.getMotherBoard(), name_, calcDescription(mode), 5, INPUT_RATE, false)
125  , debuggable(config.getMotherBoard(), getName())
126  , deformTimer(time)
127  , currentChipMode(mode)
128 {
129  // Make valgrind happy
130  ranges::fill(orgPeriod, 0);
131 
132  powerUp(time);
133  registerSound(config);
134 }
135 
137 {
138  unregisterSound();
139 }
140 
141 void SCC::powerUp(EmuTime::param time)
142 {
143  // Power on values, tested by enen (log from IRC #openmsx):
144  //
145  // <enen> wouter_: i did an scc poweron values test, deform=0,
146  // amplitude=full, channelenable=0, period=under 8
147  // ...
148  // <wouter_> did you test the value of the waveforms as well?
149  // ...
150  // <enen> filled with $FF, some bits cleared but that seems random
151 
152  // Initialize ch_enable, deform (initialize this before period)
153  reset(time);
154 
155  // Initialize waveforms (initialize before volumes)
156  for (auto& w1 : wave) {
157  ranges::fill(w1, ~0);
158  }
159  // Initialize volume (initialize this before period)
160  for (auto i : xrange(5)) {
161  setFreqVol(i + 10, 15, time);
162  }
163  // Actual initial value is difficult to measure, assume zero
164  // (initialize before period)
165  ranges::fill(pos, 0);
166 
167  // Initialize period (sets members orgPeriod, period, incr, count, out)
168  for (auto i : xrange(2 * 5)) {
169  setFreqVol(i, 0, time);
170  }
171 }
172 
173 void SCC::reset(EmuTime::param /*time*/)
174 {
175  if (currentChipMode != SCC_Real) {
177  }
178 
179  setDeformRegHelper(0);
180  ch_enable = 0;
181 }
182 
184 {
185  if (currentChipMode == SCC_Real) {
186  assert(newMode == SCC_Real);
187  } else {
188  assert(newMode != SCC_Real);
189  }
190  currentChipMode = newMode;
191 }
192 
193 byte SCC::readMem(byte addr, EmuTime::param time)
194 {
195  // Deform-register locations:
196  // SCC_Real: 0xE0..0xFF
197  // SCC_Compatible: 0xC0..0xDF
198  // SCC_plusmode: 0xC0..0xDF
199  if (((currentChipMode == SCC_Real) && (addr >= 0xE0)) ||
200  ((currentChipMode != SCC_Real) && (0xC0 <= addr) && (addr < 0xE0))) {
201  setDeformReg(0xFF, time);
202  }
203  return peekMem(addr, time);
204 }
205 
206 byte SCC::peekMem(byte address, EmuTime::param time) const
207 {
208  switch (currentChipMode) {
209  case SCC_Real:
210  if (address < 0x80) {
211  // 0x00..0x7F : read wave form 1..4
212  return readWave(address >> 5, address, time);
213  } else {
214  // 0x80..0x9F : freq volume block, write only
215  // 0xA0..0xDF : no function
216  // 0xE0..0xFF : deformation register
217  return 0xFF;
218  }
219  case SCC_Compatible:
220  if (address < 0x80) {
221  // 0x00..0x7F : read wave form 1..4
222  return readWave(address >> 5, address, time);
223  } else if (address < 0xA0) {
224  // 0x80..0x9F : freq volume block
225  return 0xFF;
226  } else if (address < 0xC0) {
227  // 0xA0..0xBF : read wave form 5
228  return readWave(4, address, time);
229  } else {
230  // 0xC0..0xDF : deformation register
231  // 0xE0..0xFF : no function
232  return 0xFF;
233  }
234  case SCC_plusmode:
235  if (address < 0xA0) {
236  // 0x00..0x9F : read wave form 1..5
237  return readWave(address >> 5, address, time);
238  } else {
239  // 0xA0..0xBF : freq volume block
240  // 0xC0..0xDF : deformation register
241  // 0xE0..0xFF : no function
242  return 0xFF;
243  }
244  default:
245  UNREACHABLE; return 0;
246  }
247 }
248 
249 byte SCC::readWave(unsigned channel, unsigned address, EmuTime::param time) const
250 {
251  if (!rotate[channel]) {
252  return wave[channel][address & 0x1F];
253  } else {
254  unsigned ticks = deformTimer.getTicksTill(time);
255  unsigned periodCh = ((channel == 3) &&
256  (currentChipMode != SCC_plusmode) &&
257  ((deformValue & 0xC0) == 0x40))
258  ? 4 : channel;
259  unsigned shift = ticks / (period[periodCh] + 1);
260  return wave[channel][(address + shift) & 0x1F];
261  }
262 }
263 
264 
265 byte SCC::getFreqVol(unsigned address) const
266 {
267  address &= 0x0F;
268  if (address < 0x0A) {
269  // get frequency
270  unsigned channel = address / 2;
271  if (address & 1) {
272  return orgPeriod[channel] >> 8;
273  } else {
274  return orgPeriod[channel] & 0xFF;
275  }
276  } else if (address < 0x0F) {
277  // get volume
278  return volume[address - 0xA];
279  } else {
280  // get enable-bits
281  return ch_enable;
282  }
283 }
284 
285 void SCC::writeMem(byte address, byte value, EmuTime::param time)
286 {
287  updateStream(time);
288 
289  switch (currentChipMode) {
290  case SCC_Real:
291  if (address < 0x80) {
292  // 0x00..0x7F : write wave form 1..4
293  writeWave(address >> 5, address, value);
294  } else if (address < 0xA0) {
295  // 0x80..0x9F : freq volume block
296  setFreqVol(address, value, time);
297  } else if (address < 0xE0) {
298  // 0xA0..0xDF : no function
299  } else {
300  // 0xE0..0xFF : deformation register
301  setDeformReg(value, time);
302  }
303  break;
304  case SCC_Compatible:
305  if (address < 0x80) {
306  // 0x00..0x7F : write wave form 1..4
307  writeWave(address >> 5, address, value);
308  } else if (address < 0xA0) {
309  // 0x80..0x9F : freq volume block
310  setFreqVol(address, value, time);
311  } else if (address < 0xC0) {
312  // 0xA0..0xBF : ignore write wave form 5
313  } else if (address < 0xE0) {
314  // 0xC0..0xDF : deformation register
315  setDeformReg(value, time);
316  } else {
317  // 0xE0..0xFF : no function
318  }
319  break;
320  case SCC_plusmode:
321  if (address < 0xA0) {
322  // 0x00..0x9F : write wave form 1..5
323  writeWave(address >> 5, address, value);
324  } else if (address < 0xC0) {
325  // 0xA0..0xBF : freq volume block
326  setFreqVol(address, value, time);
327  } else if (address < 0xE0) {
328  // 0xC0..0xDF : deformation register
329  setDeformReg(value, time);
330  } else {
331  // 0xE0..0xFF : no function
332  }
333  break;
334  default:
335  UNREACHABLE;
336  }
337 }
338 
339 float SCC::getAmplificationFactorImpl() const
340 {
341  return 1.0f / 128.0f;
342 }
343 
344 static constexpr float adjust(signed char wav, byte vol)
345 {
346  // The result is an integer value, but we store it as a float because
347  // then we need fewer int->float conversion (compared to converting in
348  // generateChannels()).
349  return float((int(wav) * vol) >> 4);
350 }
351 
352 void SCC::writeWave(unsigned channel, unsigned address, byte value)
353 {
354  // write to channel 5 only possible in SCC+ mode
355  assert(channel < 5);
356  assert((channel != 4) || (currentChipMode == SCC_plusmode));
357 
358  if (!readOnly[channel]) {
359  unsigned p = address & 0x1F;
360  wave[channel][p] = value;
361  volAdjustedWave[channel][p] = adjust(value, volume[channel]);
362  if ((currentChipMode != SCC_plusmode) && (channel == 3)) {
363  // copy waveform 4 -> waveform 5
364  wave[4][p] = wave[3][p];
365  volAdjustedWave[4][p] = adjust(value, volume[4]);
366  }
367  }
368 }
369 
370 void SCC::setFreqVol(unsigned address, byte value, EmuTime::param time)
371 {
372  address &= 0x0F; // region is visible twice
373  if (address < 0x0A) {
374  // change frequency
375  unsigned channel = address / 2;
376  unsigned per =
377  (address & 1)
378  ? ((value & 0xF) << 8) | (orgPeriod[channel] & 0xFF)
379  : (orgPeriod[channel] & 0xF00) | (value & 0xFF);
380  orgPeriod[channel] = per;
381  if (deformValue & 2) {
382  // 8 bit frequency
383  per &= 0xFF;
384  } else if (deformValue & 1) {
385  // 4 bit frequency
386  per >>= 8;
387  }
388  period[channel] = per;
389  incr[channel] = (per <= 8) ? 0 : 32;
390  count[channel] = 0; // reset to begin of byte
391  if (deformValue & 0x20) {
392  pos[channel] = 0; // reset to begin of waveform
393  // also 'rotation' mode (confirmed by test based on
394  // Artag's SCC sample player)
395  deformTimer.advance(time);
396  }
397  // after a freq change, update the output
398  out[channel] = volAdjustedWave[channel][pos[channel]];
399  } else if (address < 0x0F) {
400  // change volume
401  unsigned channel = address - 0x0A;
402  volume[channel] = value & 0xF;
403  for (auto i : xrange(32)) {
404  volAdjustedWave[channel][i] =
405  adjust(wave[channel][i], volume[channel]);
406  }
407  } else {
408  // change enable-bits
409  ch_enable = value;
410  }
411 }
412 
413 void SCC::setDeformReg(byte value, EmuTime::param time)
414 {
415  if (value == deformValue) {
416  return;
417  }
418  deformTimer.advance(time);
419  setDeformRegHelper(value);
420 }
421 
422 void SCC::setDeformRegHelper(byte value)
423 {
424  deformValue = value;
425  if (currentChipMode != SCC_Real) {
426  value &= ~0x80;
427  }
428  switch (value & 0xC0) {
429  case 0x00:
430  ranges::fill(rotate, false);
431  ranges::fill(readOnly, false);
432  break;
433  case 0x40:
434  ranges::fill(rotate, true);
435  ranges::fill(readOnly, true);
436  break;
437  case 0x80:
438  for (auto i : xrange(3)) {
439  rotate[i] = false;
440  readOnly[i] = false;
441  }
442  for (auto i : xrange(3, 5)) {
443  rotate[i] = true;
444  readOnly[i] = true;
445  }
446  break;
447  case 0xC0:
448  for (auto i : xrange(3)) {
449  rotate[i] = true;
450  readOnly[i] = true;
451  }
452  for (auto i : xrange(3, 5)) {
453  rotate[i] = false;
454  readOnly[i] = true;
455  }
456  break;
457  default:
458  UNREACHABLE;
459  }
460 }
461 
462 void SCC::generateChannels(float** bufs, unsigned num)
463 {
464  unsigned enable = ch_enable;
465  for (unsigned i = 0; i < 5; ++i, enable >>= 1) {
466  if ((enable & 1) && (volume[i] || out[i])) {
467  auto out2 = out[i];
468  unsigned count2 = count[i];
469  unsigned pos2 = pos[i];
470  unsigned incr2 = incr[i];
471  unsigned period2 = period[i] + 1;
472  for (auto j : xrange(num)) {
473  bufs[i][j] += out2;
474  count2 += incr2;
475  // Note: only for very small periods
476  // this will take more than 1 iteration
477  while (unlikely(count2 >= period2)) {
478  count2 -= period2;
479  pos2 = (pos2 + 1) % 32;
480  out2 = volAdjustedWave[i][pos2];
481  }
482  }
483  out[i] = out2;
484  count[i] = count2;
485  pos[i] = pos2;
486  } else {
487  bufs[i] = nullptr; // channel muted
488  // Update phase counter.
489  unsigned newCount = count[i] + num * incr[i];
490  count[i] = newCount % (period[i] + 1);
491  pos[i] = (pos[i] + newCount / (period[i] + 1)) % 32;
492  // Channel stays off until next waveform index.
493  out[i] = 0.0f;
494  }
495  }
496 }
497 
498 
499 // Debuggable
500 
501 SCC::Debuggable::Debuggable(MSXMotherBoard& motherBoard_, const std::string& name_)
502  : SimpleDebuggable(motherBoard_, name_ + " SCC",
503  "SCC registers in SCC+ format", 0x100)
504 {
505 }
506 
507 byte SCC::Debuggable::read(unsigned address, EmuTime::param time)
508 {
509  auto& scc = OUTER(SCC, debuggable);
510  if (address < 0xA0) {
511  // read wave form 1..5
512  return scc.readWave(address >> 5, address, time);
513  } else if (address < 0xC0) {
514  // freq volume block
515  return scc.getFreqVol(address);
516  } else if (address < 0xE0) {
517  // peek deformation register
518  return scc.deformValue;
519  } else {
520  return 0xFF;
521  }
522 }
523 
524 void SCC::Debuggable::write(unsigned address, byte value, EmuTime::param time)
525 {
526  auto& scc = OUTER(SCC, debuggable);
527  if (address < 0xA0) {
528  // read wave form 1..5
529  scc.writeWave(address >> 5, address, value);
530  } else if (address < 0xC0) {
531  // freq volume block
532  scc.setFreqVol(address, value, time);
533  } else if (address < 0xE0) {
534  // deformation register
535  scc.setDeformReg(value, time);
536  } else {
537  // ignore
538  }
539 }
540 
541 
542 static constexpr std::initializer_list<enum_string<SCC::ChipMode>> chipModeInfo = {
543  { "Real", SCC::SCC_Real },
544  { "Compatible", SCC::SCC_Compatible },
545  { "Plus", SCC::SCC_plusmode },
546 };
548 
549 template<typename Archive>
550 void SCC::serialize(Archive& ar, unsigned /*version*/)
551 {
552  ar.serialize("mode", currentChipMode,
553  "period", orgPeriod,
554  "volume", volume,
555  "ch_enable", ch_enable,
556  "deformTimer", deformTimer,
557  "deform", deformValue);
558  // multi-dimensional arrays are not directly support by the
559  // serialization framework, maybe in the future. So for now
560  // manually loop over the channels.
561  char tag[6] = { 'w', 'a', 'v', 'e', 'X', 0 };
562  for (auto [channel, wv] : enumerate(wave)) {
563  tag[4] = char('1' + channel);
564  ar.serialize(tag, wv); // signed char
565  }
566 
567  if constexpr (Archive::IS_LOADER) {
568  // recalculate volAdjustedWave
569  for (auto channel : xrange(5)) {
570  for (auto p : xrange(32)) {
571  volAdjustedWave[channel][p] =
572  adjust(wave[channel][p], volume[channel]);
573  }
574  }
575 
576  // recalculate rotate[5] and readOnly[5]
577  setDeformRegHelper(deformValue);
578 
579  // recalculate incr[5] and period[5]
580  // this also (possibly) changes count[5], pos[5] and out[5]
581  // as an unwanted side-effect, so (de)serialize those later
582  // Don't use current time, but instead use deformTimer, to
583  // avoid changing the value of deformTimer.
584  EmuTime::param time = deformTimer.getTime();
585  for (auto channel : xrange(5)) {
586  unsigned per = orgPeriod[channel];
587  setFreqVol(2 * channel + 0, (per & 0x0FF) >> 0, time);
588  setFreqVol(2 * channel + 1, (per & 0xF00) >> 8, time);
589  }
590  }
591 
592  // call to setFreqVol() modifies these variables, see above
593  ar.serialize("count", count,
594  "pos", pos,
595  "out", out); // note: changed int->float, but no need to bump serialize-version
596 }
598 
599 } // namespace openmsx
constexpr EmuTime::param getTime() const
Gets the time at which the last clock tick occurred.
Definition: Clock.hh:46
constexpr void advance(EmuTime::param e)
Advance this clock in time until the last tick which is not past the given time.
Definition: Clock.hh:110
constexpr unsigned getTicksTill(EmuTime::param e) const
Calculate the number of ticks for this clock until the given time.
Definition: Clock.hh:58
void writeMem(byte address, byte value, EmuTime::param time)
Definition: SCC.cc:285
void setChipMode(ChipMode newMode)
Definition: SCC.cc:183
void serialize(Archive &ar, unsigned version)
Definition: SCC.cc:550
void powerUp(EmuTime::param time)
Definition: SCC.cc:141
byte readMem(byte address, EmuTime::param time)
Definition: SCC.cc:193
@ SCC_Real
Definition: SCC.hh:14
@ SCC_plusmode
Definition: SCC.hh:14
@ SCC_Compatible
Definition: SCC.hh:14
void reset(EmuTime::param time)
Definition: SCC.cc:173
SCC(const std::string &name, const DeviceConfig &config, EmuTime::param time, ChipMode mode=SCC_Real)
Definition: SCC.cc:121
byte peekMem(byte address, EmuTime::param time) const
Definition: SCC.cc:206
void updateStream(EmuTime::param time)
Definition: SoundDevice.cc:139
void unregisterSound()
Unregisters this sound device with the Mixer.
Definition: SoundDevice.cc:134
void registerSound(const DeviceConfig &config)
Registers this sound device with the Mixer.
Definition: SoundDevice.cc:89
static_string_view
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
#define unlikely(x)
Definition: likely.hh:15
constexpr double round(double x)
Definition: cstd.hh:355
std::string getName(KeyCode keyCode)
Translate key code to key name.
Definition: Keys.cc:741
This file implemented 3 utility functions:
Definition: Autofire.cc:9
SERIALIZE_ENUM(CassettePlayer::State, stateInfo)
SCC
Definition: SCC.cc:597
void fill(ForwardRange &&range, const T &value)
Definition: ranges.hh:226
#define OUTER(type, member)
Definition: outer.hh:41
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:998
#define UNREACHABLE
Definition: unreachable.hh:38
constexpr auto xrange(T e)
Definition: xrange.hh:155