113static constexpr auto INPUT_RATE = unsigned(
cstd::round(3579545.0 / 32));
115static constexpr auto calcDescription(
SCC::Mode mode)
122 EmuTime::param time,
Mode mode)
124 config.getMotherBoard(), name_, calcDescription(mode), 5, INPUT_RATE, false)
125 , debuggable(config.getMotherBoard(), getName())
156 for (
auto& w1 : wave) {
160 for (
auto i :
xrange(5)) {
161 setFreqVol(i + 10, 15, time);
168 for (
auto i :
xrange(2 * 5)) {
169 setFreqVol(i, 0, time);
179 setDeformRegHelper(0);
190 currentMode = newMode;
199 if (((currentMode ==
Mode::Real) && (addr >= 0xE0)) ||
200 ((currentMode !=
Mode::Real) && (0xC0 <= addr) && (addr < 0xE0))) {
201 setDeformReg(0xFF, time);
208 switch (currentMode) {
210 if (address < 0x80) {
212 return readWave(address >> 5, address, time);
220 if (address < 0x80) {
222 return readWave(address >> 5, address, time);
223 }
else if (address < 0xA0) {
226 }
else if (address < 0xC0) {
228 return readWave(4, address, time);
235 if (address < 0xA0) {
237 return readWave(address >> 5, address, time);
249uint8_t SCC::readWave(
unsigned channel,
unsigned address, EmuTime::param time)
const
251 if (!rotate[channel]) {
252 return wave[channel][address & 0x1F];
255 unsigned periodCh = ((channel == 3) &&
257 ((deformValue & 0xC0) == 0x40))
259 unsigned shift = ticks / (period[periodCh] + 1);
260 return wave[channel][(address + shift) & 0x1F];
265uint8_t SCC::getFreqVol(
unsigned address)
const
268 if (address < 0x0A) {
270 unsigned channel = address / 2;
272 return narrow_cast<uint8_t>(orgPeriod[channel] >> 8);
274 return narrow_cast<uint8_t>(orgPeriod[channel] & 0xFF);
276 }
else if (address < 0x0F) {
278 return volume[address - 0xA];
289 switch (currentMode) {
291 if (address < 0x80) {
293 writeWave(address >> 5, address, value);
294 }
else if (address < 0xA0) {
296 setFreqVol(address, value, time);
297 }
else if (address < 0xE0) {
301 setDeformReg(value, time);
305 if (address < 0x80) {
307 writeWave(address >> 5, address, value);
308 }
else if (address < 0xA0) {
310 setFreqVol(address, value, time);
311 }
else if (address < 0xC0) {
313 }
else if (address < 0xE0) {
315 setDeformReg(value, time);
321 if (address < 0xA0) {
323 writeWave(address >> 5, address, value);
324 }
else if (address < 0xC0) {
326 setFreqVol(address, value, time);
327 }
else if (address < 0xE0) {
329 setDeformReg(value, time);
339float SCC::getAmplificationFactorImpl()
const
341 return 1.0f / 128.0f;
344static constexpr float adjust(int8_t wav, uint8_t vol)
349 return float((
int(wav) * vol) >> 4);
352void SCC::writeWave(
unsigned channel,
unsigned address, uint8_t value)
356 assert((channel != 4) || (currentMode ==
Mode::Plus));
358 if (!readOnly[channel]) {
359 unsigned p = address & 0x1F;
360 auto sValue = narrow_cast<int8_t>(value);
361 wave[channel][p] = sValue;
362 volAdjustedWave[channel][p] = adjust(sValue, volume[channel]);
363 if ((currentMode !=
Mode::Plus) && (channel == 3)) {
365 wave[4][p] = wave[3][p];
366 volAdjustedWave[4][p] = adjust(sValue, volume[4]);
371void SCC::setFreqVol(
unsigned address, uint8_t value, EmuTime::param time)
374 if (address < 0x0A) {
376 unsigned channel = address / 2;
379 ? ((value & 0xF) << 8) | (orgPeriod[channel] & 0xFF)
380 : (orgPeriod[channel] & 0xF00) | (value & 0xFF);
381 orgPeriod[channel] = per;
382 if (deformValue & 2) {
385 }
else if (deformValue & 1) {
389 period[channel] = per;
390 incr[channel] = (per <= 8) ? 0 : 32;
392 if (deformValue & 0x20) {
399 out[channel] = volAdjustedWave[channel][pos[channel]];
400 }
else if (address < 0x0F) {
402 unsigned channel = address - 0x0A;
403 volume[channel] = value & 0xF;
404 for (
auto i :
xrange(32)) {
405 volAdjustedWave[channel][i] =
406 adjust(wave[channel][i], volume[channel]);
414void SCC::setDeformReg(uint8_t value, EmuTime::param time)
416 if (value == deformValue) {
420 setDeformRegHelper(value);
423void SCC::setDeformRegHelper(uint8_t value)
429 switch (value & 0xC0) {
439 for (
auto i :
xrange(3)) {
443 for (
auto i :
xrange(3, 5)) {
449 for (
auto i :
xrange(3)) {
453 for (
auto i :
xrange(3, 5)) {
463void SCC::generateChannels(std::span<float*> bufs,
unsigned num)
465 unsigned enable = ch_enable;
466 for (
unsigned i = 0; i < 5; ++i, enable >>= 1) {
467 if ((enable & 1) && (volume[i] || (out[i] != 0.0f))) {
469 unsigned count2 = count[i];
470 unsigned pos2 = pos[i];
471 unsigned incr2 = incr[i];
472 unsigned period2 = period[i] + 1;
473 for (
auto j :
xrange(num)) {
478 while (count2 >= period2) [[unlikely]] {
480 pos2 = (pos2 + 1) % 32;
481 out2 = volAdjustedWave[i][pos2];
490 unsigned newCount = count[i] + num * incr[i];
491 count[i] = newCount % (period[i] + 1);
492 pos[i] = (pos[i] + newCount / (period[i] + 1)) % 32;
503 : SimpleDebuggable(motherBoard_, name_ +
" SCC",
504 "SCC registers in SCC+ format", 0x100)
508uint8_t SCC::Debuggable::read(
unsigned address, EmuTime::param time)
510 const auto& scc =
OUTER(SCC, debuggable);
511 if (address < 0xA0) {
513 return scc.readWave(address >> 5, address, time);
514 }
else if (address < 0xC0) {
516 return scc.getFreqVol(address);
517 }
else if (address < 0xE0) {
519 return scc.deformValue;
525void SCC::Debuggable::write(
unsigned address, uint8_t value, EmuTime::param time)
527 auto& scc =
OUTER(SCC, debuggable);
528 if (address < 0xA0) {
530 scc.writeWave(address >> 5, address, value);
531 }
else if (address < 0xC0) {
533 scc.setFreqVol(address, value, time);
534 }
else if (address < 0xE0) {
536 scc.setDeformReg(value, time);
543static constexpr std::initializer_list<enum_string<SCC::Mode>> chipModeInfo = {
550template<
typename Archive>
553 ar.serialize(
"mode", currentMode,
556 "ch_enable", ch_enable,
557 "deformTimer", deformTimer,
558 "deform", deformValue);
562 std::array<char, 6> tag = {
'w',
'a',
'v',
'e',
'X', 0};
563 for (
auto [channel, wv] :
enumerate(wave)) {
564 tag[4] = char(
'1' + channel);
565 ar.serialize(tag.data(), wv);
568 if constexpr (Archive::IS_LOADER) {
570 for (
auto channel :
xrange(5)) {
571 for (
auto p :
xrange(32)) {
572 volAdjustedWave[channel][p] =
573 adjust(wave[channel][p], volume[channel]);
578 setDeformRegHelper(deformValue);
585 EmuTime::param time = deformTimer.
getTime();
586 for (
auto channel :
xrange(5)) {
587 unsigned per = orgPeriod[channel];
588 setFreqVol(2 * channel + 0, (per & 0x0FF) >> 0, time);
589 setFreqVol(2 * channel + 1, (per & 0xF00) >> 8, time);
594 ar.serialize(
"count", count,
constexpr EmuTime::param getTime() const
Gets the time at which the last clock tick occurred.
constexpr void advance(EmuTime::param e)
Advance this clock in time until the last tick which is not past the given time.
constexpr unsigned getTicksTill(EmuTime::param e) const
Calculate the number of ticks for this clock until the given time.
void setMode(Mode newMode)
void serialize(Archive &ar, unsigned version)
void powerUp(EmuTime::param time)
uint8_t readMem(uint8_t address, EmuTime::param time)
void reset(EmuTime::param time)
uint8_t peekMem(uint8_t address, EmuTime::param time) const
void writeMem(uint8_t address, uint8_t value, EmuTime::param time)
SCC(const std::string &name, const DeviceConfig &config, EmuTime::param time, Mode mode=Mode::Real)
void updateStream(EmuTime::param time)
void unregisterSound()
Unregisters this sound device with the Mixer.
void registerSound(const DeviceConfig &config)
Registers this sound device with the Mixer.
constexpr auto enumerate(Iterable &&iterable)
Heavily inspired by Nathan Reed's blog post: Python-Like enumerate() In C++17 http://reedbeta....
constexpr double round(double x)
This file implemented 3 utility functions:
constexpr void fill(ForwardRange &&range, const T &value)
#define OUTER(type, member)
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
#define SERIALIZE_ENUM(TYPE, INFO)
constexpr auto xrange(T e)