1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-06-09 17:29:36 +00:00
CLK/Components/KonamiSCC/KonamiSCC.cpp

118 lines
3.5 KiB
C++

//
// KonamiSCC.cpp
// Clock Signal
//
// Created by Thomas Harte on 06/01/2018.
// Copyright 2018 Thomas Harte. All rights reserved.
//
#include "KonamiSCC.hpp"
#include <cstring>
using namespace Konami;
SCC::SCC(Concurrency::AsyncTaskQueue<false> &task_queue) :
task_queue_(task_queue) {}
bool SCC::is_zero_level() const {
return !(channel_enable_ & 0x1f);
}
template <Outputs::Speaker::Action action>
void SCC::apply_samples(std::size_t number_of_samples, Outputs::Speaker::MonoSample *target) {
if(is_zero_level()) {
Outputs::Speaker::fill<action>(target, target + number_of_samples, Outputs::Speaker::MonoSample());
return;
}
std::size_t c = 0;
while((master_divider_&7) && c < number_of_samples) {
Outputs::Speaker::apply<action>(target[c], transient_output_level_);
master_divider_++;
c++;
}
while(c < number_of_samples) {
for(int channel = 0; channel < 5; ++channel) {
if(channels_[channel].tone_counter) channels_[channel].tone_counter--;
else {
channels_[channel].offset = (channels_[channel].offset + 1) & 0x1f;
channels_[channel].tone_counter = channels_[channel].period;
}
}
evaluate_output_volume();
for(int ic = 0; ic < 8 && c < number_of_samples; ++ic) {
Outputs::Speaker::apply<action>(target[c], transient_output_level_);
c++;
master_divider_++;
}
}
}
template void SCC::apply_samples<Outputs::Speaker::Action::Mix>(std::size_t, Outputs::Speaker::MonoSample *);
template void SCC::apply_samples<Outputs::Speaker::Action::Store>(std::size_t, Outputs::Speaker::MonoSample *);
template void SCC::apply_samples<Outputs::Speaker::Action::Ignore>(std::size_t, Outputs::Speaker::MonoSample *);
void SCC::write(uint16_t address, uint8_t value) {
address &= 0xff;
if(address < 0x80) ram_[address] = value;
task_queue_.enqueue([this, address, value] {
// Check for a write into waveform memory.
if(address < 0x80) {
waves_[address >> 5].samples[address & 0x1f] = value;
} else switch(address) {
default: break;
case 0x80: case 0x82: case 0x84: case 0x86: case 0x88: {
int channel = (address - 0x80) >> 1;
channels_[channel].period = (channels_[channel].period & ~0xff) | value;
} break;
case 0x81: case 0x83: case 0x85: case 0x87: case 0x89: {
int channel = (address - 0x80) >> 1;
channels_[channel].period = (channels_[channel].period & 0xff) | ((value & 0xf) << 8);
} break;
case 0x8a: case 0x8b: case 0x8c: case 0x8d: case 0x8e:
channels_[address - 0x8a].amplitude = value & 0xf;
break;
case 0x8f:
channel_enable_ = value;
break;
}
evaluate_output_volume();
});
}
void SCC::evaluate_output_volume() {
transient_output_level_ =
int16_t(
((
(channel_enable_ & 0x01) ? int8_t(waves_[0].samples[channels_[0].offset]) * channels_[0].amplitude : 0 +
(channel_enable_ & 0x02) ? int8_t(waves_[1].samples[channels_[1].offset]) * channels_[1].amplitude : 0 +
(channel_enable_ & 0x04) ? int8_t(waves_[2].samples[channels_[2].offset]) * channels_[2].amplitude : 0 +
(channel_enable_ & 0x08) ? int8_t(waves_[3].samples[channels_[3].offset]) * channels_[3].amplitude : 0 +
(channel_enable_ & 0x10) ? int8_t(waves_[3].samples[channels_[4].offset]) * channels_[4].amplitude : 0
) * master_volume_) / (255*15*5)
// Five channels, each with 8-bit samples and 4-bit volumes implies a natural range of 0 to 255*15*5.
);
}
void SCC::set_sample_volume_range(std::int16_t range) {
master_volume_ = range;
evaluate_output_volume();
}
uint8_t SCC::read(uint16_t address) {
address &= 0xff;
if(address < 0x80) {
return ram_[address];
}
return 0xff;
}