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