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 using std::string;
112 
113 namespace openmsx {
114 
115 static constexpr auto INPUT_RATE = unsigned(cstd::round(3579545.0 / 32));
116 
117 static constexpr auto calcDescription(SCC::ChipMode mode)
118 {
119  return (mode == SCC::SCC_Real) ? static_string_view("Konami SCC")
120  : static_string_view("Konami SCC+");
121 }
122 
123 SCC::SCC(const string& name_, const DeviceConfig& config,
124  EmuTime::param time, ChipMode mode)
126  config.getMotherBoard(), name_, calcDescription(mode), 5, INPUT_RATE, false)
127  , debuggable(config.getMotherBoard(), getName())
128  , deformTimer(time)
129  , currentChipMode(mode)
130 {
131  // Make valgrind happy
132  ranges::fill(orgPeriod, 0);
133 
134  powerUp(time);
135  registerSound(config);
136 }
137 
139 {
140  unregisterSound();
141 }
142 
143 void SCC::powerUp(EmuTime::param time)
144 {
145  // Power on values, tested by enen (log from IRC #openmsx):
146  //
147  // <enen> wouter_: i did an scc poweron values test, deform=0,
148  // amplitude=full, channelenable=0, period=under 8
149  // ...
150  // <wouter_> did you test the value of the waveforms as well?
151  // ...
152  // <enen> filled with $FF, some bits cleared but that seems random
153 
154  // Initialize ch_enable, deform (initialize this before period)
155  reset(time);
156 
157  // Initialize waveforms (initialize before volumes)
158  for (auto& w1 : wave) {
159  ranges::fill(w1, ~0);
160  }
161  // Initialize volume (initialize this before period)
162  for (auto i : xrange(5)) {
163  setFreqVol(i + 10, 15, time);
164  }
165  // Actual initial value is difficult to measure, assume zero
166  // (initialize before period)
167  ranges::fill(pos, 0);
168 
169  // Initialize period (sets members orgPeriod, period, incr, count, out)
170  for (auto i : xrange(2 * 5)) {
171  setFreqVol(i, 0, time);
172  }
173 }
174 
175 void SCC::reset(EmuTime::param /*time*/)
176 {
177  if (currentChipMode != SCC_Real) {
179  }
180 
181  setDeformRegHelper(0);
182  ch_enable = 0;
183 }
184 
186 {
187  if (currentChipMode == SCC_Real) {
188  assert(newMode == SCC_Real);
189  } else {
190  assert(newMode != SCC_Real);
191  }
192  currentChipMode = newMode;
193 }
194 
195 byte SCC::readMem(byte addr, EmuTime::param time)
196 {
197  // Deform-register locations:
198  // SCC_Real: 0xE0..0xFF
199  // SCC_Compatible: 0xC0..0xDF
200  // SCC_plusmode: 0xC0..0xDF
201  if (((currentChipMode == SCC_Real) && (addr >= 0xE0)) ||
202  ((currentChipMode != SCC_Real) && (0xC0 <= addr) && (addr < 0xE0))) {
203  setDeformReg(0xFF, time);
204  }
205  return peekMem(addr, time);
206 }
207 
208 byte SCC::peekMem(byte address, EmuTime::param time) const
209 {
210  switch (currentChipMode) {
211  case SCC_Real:
212  if (address < 0x80) {
213  // 0x00..0x7F : read wave form 1..4
214  return readWave(address >> 5, address, time);
215  } else {
216  // 0x80..0x9F : freq volume block, write only
217  // 0xA0..0xDF : no function
218  // 0xE0..0xFF : deformation register
219  return 0xFF;
220  }
221  case SCC_Compatible:
222  if (address < 0x80) {
223  // 0x00..0x7F : read wave form 1..4
224  return readWave(address >> 5, address, time);
225  } else if (address < 0xA0) {
226  // 0x80..0x9F : freq volume block
227  return 0xFF;
228  } else if (address < 0xC0) {
229  // 0xA0..0xBF : read wave form 5
230  return readWave(4, address, time);
231  } else {
232  // 0xC0..0xDF : deformation register
233  // 0xE0..0xFF : no function
234  return 0xFF;
235  }
236  case SCC_plusmode:
237  if (address < 0xA0) {
238  // 0x00..0x9F : read wave form 1..5
239  return readWave(address >> 5, address, time);
240  } else {
241  // 0xA0..0xBF : freq volume block
242  // 0xC0..0xDF : deformation register
243  // 0xE0..0xFF : no function
244  return 0xFF;
245  }
246  default:
247  UNREACHABLE; return 0;
248  }
249 }
250 
251 byte SCC::readWave(unsigned channel, unsigned address, EmuTime::param time) const
252 {
253  if (!rotate[channel]) {
254  return wave[channel][address & 0x1F];
255  } else {
256  unsigned ticks = deformTimer.getTicksTill(time);
257  unsigned periodCh = ((channel == 3) &&
258  (currentChipMode != SCC_plusmode) &&
259  ((deformValue & 0xC0) == 0x40))
260  ? 4 : channel;
261  unsigned shift = ticks / (period[periodCh] + 1);
262  return wave[channel][(address + shift) & 0x1F];
263  }
264 }
265 
266 
267 byte SCC::getFreqVol(unsigned address) const
268 {
269  address &= 0x0F;
270  if (address < 0x0A) {
271  // get frequency
272  unsigned channel = address / 2;
273  if (address & 1) {
274  return orgPeriod[channel] >> 8;
275  } else {
276  return orgPeriod[channel] & 0xFF;
277  }
278  } else if (address < 0x0F) {
279  // get volume
280  return volume[address - 0xA];
281  } else {
282  // get enable-bits
283  return ch_enable;
284  }
285 }
286 
287 void SCC::writeMem(byte address, byte value, EmuTime::param time)
288 {
289  updateStream(time);
290 
291  switch (currentChipMode) {
292  case SCC_Real:
293  if (address < 0x80) {
294  // 0x00..0x7F : write wave form 1..4
295  writeWave(address >> 5, address, value);
296  } else if (address < 0xA0) {
297  // 0x80..0x9F : freq volume block
298  setFreqVol(address, value, time);
299  } else if (address < 0xE0) {
300  // 0xA0..0xDF : no function
301  } else {
302  // 0xE0..0xFF : deformation register
303  setDeformReg(value, time);
304  }
305  break;
306  case SCC_Compatible:
307  if (address < 0x80) {
308  // 0x00..0x7F : write wave form 1..4
309  writeWave(address >> 5, address, value);
310  } else if (address < 0xA0) {
311  // 0x80..0x9F : freq volume block
312  setFreqVol(address, value, time);
313  } else if (address < 0xC0) {
314  // 0xA0..0xBF : ignore write wave form 5
315  } else if (address < 0xE0) {
316  // 0xC0..0xDF : deformation register
317  setDeformReg(value, time);
318  } else {
319  // 0xE0..0xFF : no function
320  }
321  break;
322  case SCC_plusmode:
323  if (address < 0xA0) {
324  // 0x00..0x9F : write wave form 1..5
325  writeWave(address >> 5, address, value);
326  } else if (address < 0xC0) {
327  // 0xA0..0xBF : freq volume block
328  setFreqVol(address, value, time);
329  } else if (address < 0xE0) {
330  // 0xC0..0xDF : deformation register
331  setDeformReg(value, time);
332  } else {
333  // 0xE0..0xFF : no function
334  }
335  break;
336  default:
337  UNREACHABLE;
338  }
339 }
340 
341 float SCC::getAmplificationFactorImpl() const
342 {
343  return 1.0f / 128.0f;
344 }
345 
346 static constexpr float adjust(signed char wav, byte vol)
347 {
348  // The result is an integer value, but we store it as a float because
349  // then we need fewer int->float conversion (compared to converting in
350  // generateChannels()).
351  return float((int(wav) * vol) >> 4);
352 }
353 
354 void SCC::writeWave(unsigned channel, unsigned address, byte value)
355 {
356  // write to channel 5 only possible in SCC+ mode
357  assert(channel < 5);
358  assert((channel != 4) || (currentChipMode == SCC_plusmode));
359 
360  if (!readOnly[channel]) {
361  unsigned p = address & 0x1F;
362  wave[channel][p] = value;
363  volAdjustedWave[channel][p] = adjust(value, volume[channel]);
364  if ((currentChipMode != SCC_plusmode) && (channel == 3)) {
365  // copy waveform 4 -> waveform 5
366  wave[4][p] = wave[3][p];
367  volAdjustedWave[4][p] = adjust(value, volume[4]);
368  }
369  }
370 }
371 
372 void SCC::setFreqVol(unsigned address, byte value, EmuTime::param time)
373 {
374  address &= 0x0F; // region is visible twice
375  if (address < 0x0A) {
376  // change frequency
377  unsigned channel = address / 2;
378  unsigned per =
379  (address & 1)
380  ? ((value & 0xF) << 8) | (orgPeriod[channel] & 0xFF)
381  : (orgPeriod[channel] & 0xF00) | (value & 0xFF);
382  orgPeriod[channel] = per;
383  if (deformValue & 2) {
384  // 8 bit frequency
385  per &= 0xFF;
386  } else if (deformValue & 1) {
387  // 4 bit frequency
388  per >>= 8;
389  }
390  period[channel] = per;
391  incr[channel] = (per <= 8) ? 0 : 32;
392  count[channel] = 0; // reset to begin of byte
393  if (deformValue & 0x20) {
394  pos[channel] = 0; // reset to begin of waveform
395  // also 'rotation' mode (confirmed by test based on
396  // Artag's SCC sample player)
397  deformTimer.advance(time);
398  }
399  // after a freq change, update the output
400  out[channel] = volAdjustedWave[channel][pos[channel]];
401  } else if (address < 0x0F) {
402  // change volume
403  unsigned channel = address - 0x0A;
404  volume[channel] = value & 0xF;
405  for (auto i : xrange(32)) {
406  volAdjustedWave[channel][i] =
407  adjust(wave[channel][i], volume[channel]);
408  }
409  } else {
410  // change enable-bits
411  ch_enable = value;
412  }
413 }
414 
415 void SCC::setDeformReg(byte value, EmuTime::param time)
416 {
417  if (value == deformValue) {
418  return;
419  }
420  deformTimer.advance(time);
421  setDeformRegHelper(value);
422 }
423 
424 void SCC::setDeformRegHelper(byte value)
425 {
426  deformValue = value;
427  if (currentChipMode != SCC_Real) {
428  value &= ~0x80;
429  }
430  switch (value & 0xC0) {
431  case 0x00:
432  ranges::fill(rotate, false);
433  ranges::fill(readOnly, false);
434  break;
435  case 0x40:
436  ranges::fill(rotate, true);
437  ranges::fill(readOnly, true);
438  break;
439  case 0x80:
440  for (auto i : xrange(3)) {
441  rotate[i] = false;
442  readOnly[i] = false;
443  }
444  for (auto i : xrange(3, 5)) {
445  rotate[i] = true;
446  readOnly[i] = true;
447  }
448  break;
449  case 0xC0:
450  for (auto i : xrange(3)) {
451  rotate[i] = true;
452  readOnly[i] = true;
453  }
454  for (auto i : xrange(3, 5)) {
455  rotate[i] = false;
456  readOnly[i] = true;
457  }
458  break;
459  default:
460  UNREACHABLE;
461  }
462 }
463 
464 void SCC::generateChannels(float** bufs, unsigned num)
465 {
466  unsigned enable = ch_enable;
467  for (unsigned i = 0; i < 5; ++i, enable >>= 1) {
468  if ((enable & 1) && (volume[i] || out[i])) {
469  auto out2 = out[i];
470  unsigned count2 = count[i];
471  unsigned pos2 = pos[i];
472  unsigned incr2 = incr[i];
473  unsigned period2 = period[i] + 1;
474  for (auto j : xrange(num)) {
475  bufs[i][j] += out2;
476  count2 += incr2;
477  // Note: only for very small periods
478  // this will take more than 1 iteration
479  while (unlikely(count2 >= period2)) {
480  count2 -= period2;
481  pos2 = (pos2 + 1) % 32;
482  out2 = volAdjustedWave[i][pos2];
483  }
484  }
485  out[i] = out2;
486  count[i] = count2;
487  pos[i] = pos2;
488  } else {
489  bufs[i] = nullptr; // channel muted
490  // Update phase counter.
491  unsigned newCount = count[i] + num * incr[i];
492  count[i] = newCount % (period[i] + 1);
493  pos[i] = (pos[i] + newCount / (period[i] + 1)) % 32;
494  // Channel stays off until next waveform index.
495  out[i] = 0.0f;
496  }
497  }
498 }
499 
500 
501 // Debuggable
502 
503 SCC::Debuggable::Debuggable(MSXMotherBoard& motherBoard_, const string& name_)
504  : SimpleDebuggable(motherBoard_, name_ + " SCC",
505  "SCC registers in SCC+ format", 0x100)
506 {
507 }
508 
509 byte SCC::Debuggable::read(unsigned address, EmuTime::param time)
510 {
511  auto& scc = OUTER(SCC, debuggable);
512  if (address < 0xA0) {
513  // read wave form 1..5
514  return scc.readWave(address >> 5, address, time);
515  } else if (address < 0xC0) {
516  // freq volume block
517  return scc.getFreqVol(address);
518  } else if (address < 0xE0) {
519  // peek deformation register
520  return scc.deformValue;
521  } else {
522  return 0xFF;
523  }
524 }
525 
526 void SCC::Debuggable::write(unsigned address, byte value, EmuTime::param time)
527 {
528  auto& scc = OUTER(SCC, debuggable);
529  if (address < 0xA0) {
530  // read wave form 1..5
531  scc.writeWave(address >> 5, address, value);
532  } else if (address < 0xC0) {
533  // freq volume block
534  scc.setFreqVol(address, value, time);
535  } else if (address < 0xE0) {
536  // deformation register
537  scc.setDeformReg(value, time);
538  } else {
539  // ignore
540  }
541 }
542 
543 
544 static constexpr std::initializer_list<enum_string<SCC::ChipMode>> chipModeInfo = {
545  { "Real", SCC::SCC_Real },
546  { "Compatible", SCC::SCC_Compatible },
547  { "Plus", SCC::SCC_plusmode },
548 };
550 
551 template<typename Archive>
552 void SCC::serialize(Archive& ar, unsigned /*version*/)
553 {
554  ar.serialize("mode", currentChipMode,
555  "period", orgPeriod,
556  "volume", volume,
557  "ch_enable", ch_enable,
558  "deformTimer", deformTimer,
559  "deform", deformValue);
560  // multi-dimensional arrays are not directly support by the
561  // serialization framework, maybe in the future. So for now
562  // manually loop over the channels.
563  char tag[6] = { 'w', 'a', 'v', 'e', 'X', 0 };
564  for (auto [channel, wv] : enumerate(wave)) {
565  tag[4] = char('1' + channel);
566  ar.serialize(tag, wv); // signed char
567  }
568 
569  if constexpr (Archive::IS_LOADER) {
570  // recalculate volAdjustedWave
571  for (auto channel : xrange(5)) {
572  for (auto p : xrange(32)) {
573  volAdjustedWave[channel][p] =
574  adjust(wave[channel][p], volume[channel]);
575  }
576  }
577 
578  // recalculate rotate[5] and readOnly[5]
579  setDeformRegHelper(deformValue);
580 
581  // recalculate incr[5] and period[5]
582  // this also (possibly) changes count[5], pos[5] and out[5]
583  // as an unwanted side-effect, so (de)serialize those later
584  // Don't use current time, but instead use deformTimer, to
585  // avoid changing the value of deformTimer.
586  EmuTime::param time = deformTimer.getTime();
587  for (auto channel : xrange(5)) {
588  unsigned per = orgPeriod[channel];
589  setFreqVol(2 * channel + 0, (per & 0x0FF) >> 0, time);
590  setFreqVol(2 * channel + 1, (per & 0xF00) >> 8, time);
591  }
592  }
593 
594  // call to setFreqVol() modifies these variables, see above
595  ar.serialize("count", count,
596  "pos", pos,
597  "out", out); // note: changed int->float, but no need to bump serialize-version
598 }
600 
601 } // 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:287
void setChipMode(ChipMode newMode)
Definition: SCC.cc:185
void serialize(Archive &ar, unsigned version)
Definition: SCC.cc:552
void powerUp(EmuTime::param time)
Definition: SCC.cc:143
byte readMem(byte address, EmuTime::param time)
Definition: SCC.cc:195
@ 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:175
SCC(const std::string &name, const DeviceConfig &config, EmuTime::param time, ChipMode mode=SCC_Real)
Definition: SCC.cc:123
byte peekMem(byte address, EmuTime::param time) const
Definition: SCC.cc:208
void updateStream(EmuTime::param time)
Definition: SoundDevice.cc:141
void unregisterSound()
Unregisters this sound device with the Mixer.
Definition: SoundDevice.cc:136
void registerSound(const DeviceConfig &config)
Registers this sound device with the Mixer.
Definition: SoundDevice.cc:92
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
string getName(KeyCode keyCode)
Translate key code to key name.
Definition: Keys.cc:742
This file implemented 3 utility functions:
Definition: Autofire.cc:9
SERIALIZE_ENUM(CassettePlayer::State, stateInfo)
SCC
Definition: SCC.cc:599
void fill(ForwardRange &&range, const T &value)
Definition: ranges.hh:197
#define OUTER(type, member)
Definition: outer.hh:41
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:983
#define UNREACHABLE
Definition: unreachable.hh:38
constexpr auto xrange(T e)
Definition: xrange.hh:155