From 7890506b16505b8d37eb7565caa879e1af58241a Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 28 Feb 2018 22:36:03 -0500 Subject: [PATCH] Gives the SN76489 its proper dividers and personalities. --- Components/SN76489/SN76489.cpp | 25 ++++++++++++++----- Components/SN76489/SN76489.hpp | 13 +++++++--- Machines/ColecoVision/ColecoVision.cpp | 34 ++++++++++++-------------- 3 files changed, 45 insertions(+), 27 deletions(-) diff --git a/Components/SN76489/SN76489.cpp b/Components/SN76489/SN76489.cpp index 2cf081f3c..495a43607 100644 --- a/Components/SN76489/SN76489.cpp +++ b/Components/SN76489/SN76489.cpp @@ -12,7 +12,7 @@ using namespace TI; -SN76489::SN76489(Concurrency::DeferringAsyncTaskQueue &task_queue) : task_queue_(task_queue) { +SN76489::SN76489(Personality personality, Concurrency::DeferringAsyncTaskQueue &task_queue) : task_queue_(task_queue) { // Build a volume table. double multiplier = pow(10.0, -0.1); double volume = 8191.0f; @@ -22,6 +22,21 @@ SN76489::SN76489(Concurrency::DeferringAsyncTaskQueue &task_queue) : task_queue_ } volumes_[15] = 0; evaluate_output_volume(); + + switch(personality) { + case Personality::SN76494: + master_divider_period_ = 2; + shifter_is_16bit_ = false; + break; + case Personality::SN76489: + master_divider_period_ = 16; + shifter_is_16bit_ = false; + break; + case Personality::SMS: + master_divider_period_ = 16; + shifter_is_16bit_ = true; + break; + } } void SN76489::set_register(uint8_t value) { @@ -72,10 +87,8 @@ void SN76489::evaluate_output_volume() { } void SN76489::get_samples(std::size_t number_of_samples, std::int16_t *target) { - // For now: assume a divide by eight. - std::size_t c = 0; - while((master_divider_&7) && c < number_of_samples) { + while((master_divider_& (master_divider_period_ - 1)) && c < number_of_samples) { target[c] = output_volume_; master_divider_++; c++; @@ -124,12 +137,12 @@ void SN76489::get_samples(std::size_t number_of_samples, std::int16_t *target) { evaluate_output_volume(); - for(int ic = 0; ic < 8 && c < number_of_samples; ic++) { + for(int ic = 0; ic < master_divider_period_ && c < number_of_samples; ++ic) { target[c] = output_volume_; c++; master_divider_++; } } - master_divider_ &= 7; + master_divider_ &= (master_divider_period_ - 1); } diff --git a/Components/SN76489/SN76489.hpp b/Components/SN76489/SN76489.hpp index 409df0428..eb58af331 100644 --- a/Components/SN76489/SN76489.hpp +++ b/Components/SN76489/SN76489.hpp @@ -16,8 +16,14 @@ namespace TI { class SN76489: public Outputs::Speaker::SampleSource { public: + enum class Personality { + SN76489, + SN76494, + SMS + }; + /// Creates a new SN76489. - SN76489(Concurrency::DeferringAsyncTaskQueue &task_queue); + SN76489(Personality personality, Concurrency::DeferringAsyncTaskQueue &task_queue); /// Writes a new value to the SN76489. void set_register(uint8_t value); @@ -26,8 +32,9 @@ class SN76489: public Outputs::Speaker::SampleSource { void get_samples(std::size_t number_of_samples, std::int16_t *target); private: - std::size_t master_divider_ = 0; - int16_t output_volume_ = 0;; + int master_divider_ = 0; + int master_divider_period_ = 16; + int16_t output_volume_ = 0; void evaluate_output_volume(); int volumes_[16]; diff --git a/Machines/ColecoVision/ColecoVision.cpp b/Machines/ColecoVision/ColecoVision.cpp index 40b569f52..77ecded30 100644 --- a/Machines/ColecoVision/ColecoVision.cpp +++ b/Machines/ColecoVision/ColecoVision.cpp @@ -107,9 +107,10 @@ class ConcreteMachine: public: ConcreteMachine() : z80_(*this), - sn76489_(audio_queue_), + sn76489_(TI::SN76489::Personality::SN76489, audio_queue_), speaker_(sn76489_) { - speaker_.set_input_rate(3579545.0f / 2.0f); // TODO: try to find out whether this is correct. +// speaker_.set_input_rate(3579545.0f); + speaker_.set_input_rate(3579545.0f / 2.0f); set_clock_rate(3579545); joysticks_.emplace_back(new Joystick); joysticks_.emplace_back(new Joystick); @@ -158,9 +159,7 @@ class ConcreteMachine: bool set_rom_fetcher(const std::function>>(const std::string &machine, const std::vector &names)> &roms_with_names) override { auto roms = roms_with_names( "ColecoVision", - { - "coleco.rom" - }); + { "coleco.rom" }); if(!roms[0]) return false; @@ -172,18 +171,6 @@ class ConcreteMachine: // MARK: Z80::BusHandler forceinline HalfCycles perform_machine_cycle(const CPU::Z80::PartialMachineCycle &cycle) { - time_since_vdp_update_ += cycle.length; - time_since_sn76489_update_ += cycle.length; - - if(time_until_interrupt_ > 0) { - time_until_interrupt_ -= cycle.length; - if(time_until_interrupt_ <= HalfCycles(0)) { - z80_.set_non_maskable_interrupt_line(true, time_until_interrupt_); - update_video(); - time_until_interrupt_ = vdp_->get_time_until_interrupt(); - } - } - uint16_t address = cycle.address ? *cycle.address : 0x0000; switch(cycle.operation) { case CPU::Z80::PartialMachineCycle::ReadOpcode: @@ -256,11 +243,21 @@ class ConcreteMachine: default: break; } + time_since_vdp_update_ += cycle.length; + time_since_sn76489_update_ += cycle.length; + + if(time_until_interrupt_ > 0) { + time_until_interrupt_ -= cycle.length; + if(time_until_interrupt_ <= HalfCycles(0)) { + z80_.set_non_maskable_interrupt_line(true, time_until_interrupt_); + } + } + return HalfCycles(0); } void flush() { - vdp_->run_for(time_since_vdp_update_.flush()); + update_video(); update_audio(); audio_queue_.perform(); } @@ -268,6 +265,7 @@ class ConcreteMachine: private: void update_audio() { speaker_.run_for(audio_queue_, time_since_sn76489_update_.divide_cycles(Cycles(2))); +// speaker_.run_for(audio_queue_, time_since_sn76489_update_.cycles()); } void update_video() { vdp_->run_for(time_since_vdp_update_.flush());