diff --git a/Components/KonamiSCC/KonamiSCC.cpp b/Components/KonamiSCC/KonamiSCC.cpp index dc7187df8..52db6cf3f 100644 --- a/Components/KonamiSCC/KonamiSCC.cpp +++ b/Components/KonamiSCC/KonamiSCC.cpp @@ -25,14 +25,83 @@ void SCC::get_samples(std::size_t number_of_samples, std::int16_t *target) { return; } - // TODO + std::size_t c = 0; + while((master_divider_&7) && c < number_of_samples) { + target[c] = output_volume_; + 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) { + target[c] = output_volume_; + c++; + master_divider_++; + } + } } void SCC::write(uint16_t address, uint8_t value) { - // TODO + address &= 0xff; + if(address < 0x80) ram_[address] = value; + + task_queue_.defer([=] { + // 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; + break; + + case 0x8f: + channel_enable_ = value; + break; + } + + evaluate_output_volume(); + }); +} + +void SCC::evaluate_output_volume() { + output_volume_ = + static_cast( + (( + (channel_enable_ & 0x01) ? static_cast(waves_[0].samples[channels_[0].offset]) * channels_[0].amplitude : 0 + + (channel_enable_ & 0x02) ? static_cast(waves_[1].samples[channels_[1].offset]) * channels_[1].amplitude : 0 + + (channel_enable_ & 0x04) ? static_cast(waves_[2].samples[channels_[2].offset]) * channels_[2].amplitude : 0 + + (channel_enable_ & 0x08) ? static_cast(waves_[3].samples[channels_[3].offset]) * channels_[3].amplitude : 0 + + (channel_enable_ & 0x10) ? static_cast(waves_[3].samples[channels_[4].offset]) * channels_[4].amplitude : 0 + ) * 16) / 5 + ); } uint8_t SCC::read(uint16_t address) { - // TODO + address &= 0xff; + if(address < 0x80) { + return ram_[address]; + } return 0xff; } diff --git a/Components/KonamiSCC/KonamiSCC.hpp b/Components/KonamiSCC/KonamiSCC.hpp index 0bcd7b51b..31c49843b 100644 --- a/Components/KonamiSCC/KonamiSCC.hpp +++ b/Components/KonamiSCC/KonamiSCC.hpp @@ -39,19 +39,32 @@ class SCC: public ::Outputs::Speaker::SampleSource { uint8_t read(uint16_t address); private: + Concurrency::DeferringAsyncTaskQueue &task_queue_; + + // State from here on down is accessed ony from the audio thread. + int master_divider_ = 0; + int16_t output_volume_ = 0; + struct Channel { int period = 0; int amplitude = 0; + + int tone_counter = 0; + int offset = 0; } channels_[5]; struct Wavetable { - std::int16_t samples[32]; + std::uint8_t samples[32]; } waves_[4]; std::uint8_t channel_enable_ = 0; std::uint8_t test_register_ = 0; - Concurrency::DeferringAsyncTaskQueue &task_queue_; + void evaluate_output_volume(); + + // This keeps a copy of wave memory that is accessed from the + // main emulation thread. + std::uint8_t ram_[128]; }; }