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