mirror of
https://github.com/TomHarte/CLK.git
synced 2025-08-08 14:25:05 +00:00
Gives the SN76489 its proper dividers and personalities.
This commit is contained in:
@@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
using namespace TI;
|
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.
|
// Build a volume table.
|
||||||
double multiplier = pow(10.0, -0.1);
|
double multiplier = pow(10.0, -0.1);
|
||||||
double volume = 8191.0f;
|
double volume = 8191.0f;
|
||||||
@@ -22,6 +22,21 @@ SN76489::SN76489(Concurrency::DeferringAsyncTaskQueue &task_queue) : task_queue_
|
|||||||
}
|
}
|
||||||
volumes_[15] = 0;
|
volumes_[15] = 0;
|
||||||
evaluate_output_volume();
|
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) {
|
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) {
|
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;
|
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_;
|
target[c] = output_volume_;
|
||||||
master_divider_++;
|
master_divider_++;
|
||||||
c++;
|
c++;
|
||||||
@@ -124,12 +137,12 @@ void SN76489::get_samples(std::size_t number_of_samples, std::int16_t *target) {
|
|||||||
|
|
||||||
evaluate_output_volume();
|
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_;
|
target[c] = output_volume_;
|
||||||
c++;
|
c++;
|
||||||
master_divider_++;
|
master_divider_++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
master_divider_ &= 7;
|
master_divider_ &= (master_divider_period_ - 1);
|
||||||
}
|
}
|
||||||
|
@@ -16,8 +16,14 @@ namespace TI {
|
|||||||
|
|
||||||
class SN76489: public Outputs::Speaker::SampleSource {
|
class SN76489: public Outputs::Speaker::SampleSource {
|
||||||
public:
|
public:
|
||||||
|
enum class Personality {
|
||||||
|
SN76489,
|
||||||
|
SN76494,
|
||||||
|
SMS
|
||||||
|
};
|
||||||
|
|
||||||
/// Creates a new SN76489.
|
/// Creates a new SN76489.
|
||||||
SN76489(Concurrency::DeferringAsyncTaskQueue &task_queue);
|
SN76489(Personality personality, Concurrency::DeferringAsyncTaskQueue &task_queue);
|
||||||
|
|
||||||
/// Writes a new value to the SN76489.
|
/// Writes a new value to the SN76489.
|
||||||
void set_register(uint8_t value);
|
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);
|
void get_samples(std::size_t number_of_samples, std::int16_t *target);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::size_t master_divider_ = 0;
|
int master_divider_ = 0;
|
||||||
int16_t output_volume_ = 0;;
|
int master_divider_period_ = 16;
|
||||||
|
int16_t output_volume_ = 0;
|
||||||
void evaluate_output_volume();
|
void evaluate_output_volume();
|
||||||
int volumes_[16];
|
int volumes_[16];
|
||||||
|
|
||||||
|
@@ -107,9 +107,10 @@ class ConcreteMachine:
|
|||||||
public:
|
public:
|
||||||
ConcreteMachine() :
|
ConcreteMachine() :
|
||||||
z80_(*this),
|
z80_(*this),
|
||||||
sn76489_(audio_queue_),
|
sn76489_(TI::SN76489::Personality::SN76489, audio_queue_),
|
||||||
speaker_(sn76489_) {
|
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);
|
set_clock_rate(3579545);
|
||||||
joysticks_.emplace_back(new Joystick);
|
joysticks_.emplace_back(new Joystick);
|
||||||
joysticks_.emplace_back(new Joystick);
|
joysticks_.emplace_back(new Joystick);
|
||||||
@@ -158,9 +159,7 @@ class ConcreteMachine:
|
|||||||
bool set_rom_fetcher(const std::function<std::vector<std::unique_ptr<std::vector<uint8_t>>>(const std::string &machine, const std::vector<std::string> &names)> &roms_with_names) override {
|
bool set_rom_fetcher(const std::function<std::vector<std::unique_ptr<std::vector<uint8_t>>>(const std::string &machine, const std::vector<std::string> &names)> &roms_with_names) override {
|
||||||
auto roms = roms_with_names(
|
auto roms = roms_with_names(
|
||||||
"ColecoVision",
|
"ColecoVision",
|
||||||
{
|
{ "coleco.rom" });
|
||||||
"coleco.rom"
|
|
||||||
});
|
|
||||||
|
|
||||||
if(!roms[0]) return false;
|
if(!roms[0]) return false;
|
||||||
|
|
||||||
@@ -172,18 +171,6 @@ class ConcreteMachine:
|
|||||||
|
|
||||||
// MARK: Z80::BusHandler
|
// MARK: Z80::BusHandler
|
||||||
forceinline HalfCycles perform_machine_cycle(const CPU::Z80::PartialMachineCycle &cycle) {
|
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;
|
uint16_t address = cycle.address ? *cycle.address : 0x0000;
|
||||||
switch(cycle.operation) {
|
switch(cycle.operation) {
|
||||||
case CPU::Z80::PartialMachineCycle::ReadOpcode:
|
case CPU::Z80::PartialMachineCycle::ReadOpcode:
|
||||||
@@ -256,11 +243,21 @@ class ConcreteMachine:
|
|||||||
default: break;
|
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);
|
return HalfCycles(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void flush() {
|
void flush() {
|
||||||
vdp_->run_for(time_since_vdp_update_.flush());
|
update_video();
|
||||||
update_audio();
|
update_audio();
|
||||||
audio_queue_.perform();
|
audio_queue_.perform();
|
||||||
}
|
}
|
||||||
@@ -268,6 +265,7 @@ class ConcreteMachine:
|
|||||||
private:
|
private:
|
||||||
void update_audio() {
|
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_.divide_cycles(Cycles(2)));
|
||||||
|
// speaker_.run_for(audio_queue_, time_since_sn76489_update_.cycles());
|
||||||
}
|
}
|
||||||
void update_video() {
|
void update_video() {
|
||||||
vdp_->run_for(time_since_vdp_update_.flush());
|
vdp_->run_for(time_since_vdp_update_.flush());
|
||||||
|
Reference in New Issue
Block a user