mirror of
https://github.com/TomHarte/CLK.git
synced 2025-02-08 11:32:02 +00:00
Adds Super Game Module support for the ColecoVision.
This commit is contained in:
parent
7ca02be578
commit
3c5a8d9ff3
@ -108,7 +108,6 @@ void AY38910::get_samples(std::size_t number_of_samples, int16_t *target) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
evaluate_output_volume();
|
evaluate_output_volume();
|
||||||
printf("%d ", output_volume_);
|
|
||||||
|
|
||||||
for(int ic = 0; ic < 8 && c < number_of_samples; ic++) {
|
for(int ic = 0; ic < 8 && c < number_of_samples; ic++) {
|
||||||
target[c] = output_volume_;
|
target[c] = output_volume_;
|
||||||
@ -157,6 +156,11 @@ void AY38910::evaluate_output_volume() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AY38910::is_zero_level() {
|
||||||
|
// Confirm that the AY is trivially at the zero level if all three volume controls are set to fixed zero.
|
||||||
|
return output_registers_[0x8] == 0 && output_registers_[0x9] == 0 && output_registers_[0xa] == 0;
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - Register manipulation
|
// MARK: - Register manipulation
|
||||||
|
|
||||||
void AY38910::select_register(uint8_t r) {
|
void AY38910::select_register(uint8_t r) {
|
||||||
|
@ -85,6 +85,7 @@ class AY38910: public ::Outputs::Speaker::SampleSource {
|
|||||||
|
|
||||||
// to satisfy ::Outputs::Speaker (included via ::Outputs::Filter; not for public consumption
|
// to satisfy ::Outputs::Speaker (included via ::Outputs::Filter; not for public consumption
|
||||||
void get_samples(std::size_t number_of_samples, int16_t *target);
|
void get_samples(std::size_t number_of_samples, int16_t *target);
|
||||||
|
bool is_zero_level();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Concurrency::DeferringAsyncTaskQueue &task_queue_;
|
Concurrency::DeferringAsyncTaskQueue &task_queue_;
|
||||||
|
@ -15,12 +15,12 @@ using namespace Konami;
|
|||||||
SCC::SCC(Concurrency::DeferringAsyncTaskQueue &task_queue) :
|
SCC::SCC(Concurrency::DeferringAsyncTaskQueue &task_queue) :
|
||||||
task_queue_(task_queue) {}
|
task_queue_(task_queue) {}
|
||||||
|
|
||||||
bool SCC::is_silent() {
|
bool SCC::is_zero_level() {
|
||||||
return !(channel_enable_ & 0x1f);
|
return !(channel_enable_ & 0x1f);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SCC::get_samples(std::size_t number_of_samples, std::int16_t *target) {
|
void SCC::get_samples(std::size_t number_of_samples, std::int16_t *target) {
|
||||||
if(is_silent()) {
|
if(is_zero_level()) {
|
||||||
std::memset(target, 0, sizeof(std::int16_t) * number_of_samples);
|
std::memset(target, 0, sizeof(std::int16_t) * number_of_samples);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ class SCC: public ::Outputs::Speaker::SampleSource {
|
|||||||
SCC(Concurrency::DeferringAsyncTaskQueue &task_queue);
|
SCC(Concurrency::DeferringAsyncTaskQueue &task_queue);
|
||||||
|
|
||||||
/// As per ::SampleSource; provides a broadphase test for silence.
|
/// As per ::SampleSource; provides a broadphase test for silence.
|
||||||
bool is_silent();
|
bool is_zero_level();
|
||||||
|
|
||||||
/// As per ::SampleSource; provides audio output.
|
/// As per ::SampleSource; provides audio output.
|
||||||
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);
|
||||||
|
@ -82,6 +82,10 @@ void SN76489::set_register(uint8_t value) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SN76489::is_zero_level() {
|
||||||
|
return channels_[0].volume == 0xf && channels_[1].volume == 0xf && channels_[2].volume == 0xf && channels_[3].volume == 0xf;
|
||||||
|
}
|
||||||
|
|
||||||
void SN76489::evaluate_output_volume() {
|
void SN76489::evaluate_output_volume() {
|
||||||
output_volume_ = static_cast<int16_t>(
|
output_volume_ = static_cast<int16_t>(
|
||||||
channels_[0].level * volumes_[channels_[0].volume] +
|
channels_[0].level * volumes_[channels_[0].volume] +
|
||||||
|
@ -30,6 +30,7 @@ class SN76489: public Outputs::Speaker::SampleSource {
|
|||||||
|
|
||||||
// As per SampleSource.
|
// As per 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);
|
||||||
|
bool is_zero_level();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int master_divider_ = 0;
|
int master_divider_ = 0;
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
#include "../../Processors/Z80/Z80.hpp"
|
#include "../../Processors/Z80/Z80.hpp"
|
||||||
|
|
||||||
#include "../../Components/9918/9918.hpp"
|
#include "../../Components/9918/9918.hpp"
|
||||||
|
#include "../../Components/AY38910/AY38910.hpp" // For the Super Game Module.
|
||||||
#include "../../Components/SN76489/SN76489.hpp"
|
#include "../../Components/SN76489/SN76489.hpp"
|
||||||
|
|
||||||
#include "../ConfigurationTarget.hpp"
|
#include "../ConfigurationTarget.hpp"
|
||||||
@ -19,10 +20,11 @@
|
|||||||
|
|
||||||
#include "../../ClockReceiver/ForceInline.hpp"
|
#include "../../ClockReceiver/ForceInline.hpp"
|
||||||
|
|
||||||
|
#include "../../Outputs/Speaker/Implementation/CompoundSource.hpp"
|
||||||
#include "../../Outputs/Speaker/Implementation/LowpassSpeaker.hpp"
|
#include "../../Outputs/Speaker/Implementation/LowpassSpeaker.hpp"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
const int sn76489_divider = 4;
|
const int sn76489_divider = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Coleco {
|
namespace Coleco {
|
||||||
@ -112,7 +114,9 @@ class ConcreteMachine:
|
|||||||
ConcreteMachine() :
|
ConcreteMachine() :
|
||||||
z80_(*this),
|
z80_(*this),
|
||||||
sn76489_(TI::SN76489::Personality::SN76489, audio_queue_, sn76489_divider),
|
sn76489_(TI::SN76489::Personality::SN76489, audio_queue_, sn76489_divider),
|
||||||
speaker_(sn76489_) {
|
ay_(audio_queue_),
|
||||||
|
mixer_(sn76489_, ay_),
|
||||||
|
speaker_(mixer_) {
|
||||||
speaker_.set_input_rate(3579545.0f / static_cast<float>(sn76489_divider));
|
speaker_.set_input_rate(3579545.0f / static_cast<float>(sn76489_divider));
|
||||||
set_clock_rate(3579545);
|
set_clock_rate(3579545);
|
||||||
joysticks_.emplace_back(new Joystick);
|
joysticks_.emplace_back(new Joystick);
|
||||||
@ -193,7 +197,13 @@ class ConcreteMachine:
|
|||||||
case CPU::Z80::PartialMachineCycle::ReadOpcode:
|
case CPU::Z80::PartialMachineCycle::ReadOpcode:
|
||||||
case CPU::Z80::PartialMachineCycle::Read:
|
case CPU::Z80::PartialMachineCycle::Read:
|
||||||
if(address < 0x2000) {
|
if(address < 0x2000) {
|
||||||
*cycle.value = bios_[address & 0x1fff];
|
if(super_game_module_.replace_bios) {
|
||||||
|
*cycle.value = super_game_module_.ram[address];
|
||||||
|
} else {
|
||||||
|
*cycle.value = bios_[address];
|
||||||
|
}
|
||||||
|
} else if(super_game_module_.replace_ram && address < 0x8000) {
|
||||||
|
*cycle.value = super_game_module_.ram[address];
|
||||||
} else if(address >= 0x6000 && address < 0x8000) {
|
} else if(address >= 0x6000 && address < 0x8000) {
|
||||||
*cycle.value = ram_[address & 1023];
|
*cycle.value = ram_[address & 1023];
|
||||||
} else if(address >= 0x8000 && address <= cartridge_address_limit_) {
|
} else if(address >= 0x8000 && address <= cartridge_address_limit_) {
|
||||||
@ -207,7 +217,11 @@ class ConcreteMachine:
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case CPU::Z80::PartialMachineCycle::Write:
|
case CPU::Z80::PartialMachineCycle::Write:
|
||||||
if(address >= 0x6000 && address < 0x8000) {
|
if(super_game_module_.replace_bios && address < 0x2000) {
|
||||||
|
super_game_module_.ram[address] = *cycle.value;
|
||||||
|
} else if(super_game_module_.replace_ram && address >= 0x2000 && address < 0x8000) {
|
||||||
|
super_game_module_.ram[address] = *cycle.value;
|
||||||
|
} else if(address >= 0x6000 && address < 0x8000) {
|
||||||
ram_[address & 1023] = *cycle.value;
|
ram_[address & 1023] = *cycle.value;
|
||||||
} else if(is_megacart_ && address >= 0xffc0) {
|
} else if(is_megacart_ && address >= 0xffc0) {
|
||||||
page_megacart(address);
|
page_megacart(address);
|
||||||
@ -234,7 +248,15 @@ class ConcreteMachine:
|
|||||||
} break;
|
} break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
*cycle.value = 0xff;
|
switch(address&0xff) {
|
||||||
|
default: *cycle.value = 0xff; break;
|
||||||
|
case 0x52:
|
||||||
|
// Read AY data.
|
||||||
|
ay_.set_control_lines(GI::AY38910::ControlLines(GI::AY38910::BC2 | GI::AY38910::BC1));
|
||||||
|
*cycle.value = ay_.get_data_output();
|
||||||
|
ay_.set_control_lines(GI::AY38910::ControlLines(0));
|
||||||
|
break;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -258,7 +280,30 @@ class ConcreteMachine:
|
|||||||
sn76489_.set_register(*cycle.value);
|
sn76489_.set_register(*cycle.value);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default: break;
|
default:
|
||||||
|
// Catch Super Game Module accesses; it decodes more thoroughly.
|
||||||
|
switch(address&0xff) {
|
||||||
|
default: break;
|
||||||
|
case 0x7f:
|
||||||
|
super_game_module_.replace_bios = !((*cycle.value)&0x2);
|
||||||
|
break;
|
||||||
|
case 0x50:
|
||||||
|
// Set AY address.
|
||||||
|
ay_.set_control_lines(GI::AY38910::BC1);
|
||||||
|
ay_.set_data_input(*cycle.value);
|
||||||
|
ay_.set_control_lines(GI::AY38910::ControlLines(0));
|
||||||
|
break;
|
||||||
|
case 0x51:
|
||||||
|
// Set AY data.
|
||||||
|
ay_.set_control_lines(GI::AY38910::ControlLines(GI::AY38910::BC2 | GI::AY38910::BDIR));
|
||||||
|
ay_.set_data_input(*cycle.value);
|
||||||
|
ay_.set_control_lines(GI::AY38910::ControlLines(0));
|
||||||
|
break;
|
||||||
|
case 0x53:
|
||||||
|
super_game_module_.replace_ram = !!((*cycle.value)&0x1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
@ -301,7 +346,9 @@ class ConcreteMachine:
|
|||||||
|
|
||||||
Concurrency::DeferringAsyncTaskQueue audio_queue_;
|
Concurrency::DeferringAsyncTaskQueue audio_queue_;
|
||||||
TI::SN76489 sn76489_;
|
TI::SN76489 sn76489_;
|
||||||
Outputs::Speaker::LowpassSpeaker<TI::SN76489> speaker_;
|
GI::AY38910::AY38910 ay_;
|
||||||
|
Outputs::Speaker::CompoundSource<TI::SN76489, GI::AY38910::AY38910> mixer_;
|
||||||
|
Outputs::Speaker::LowpassSpeaker<Outputs::Speaker::CompoundSource<TI::SN76489, GI::AY38910::AY38910>> speaker_;
|
||||||
|
|
||||||
std::vector<uint8_t> bios_;
|
std::vector<uint8_t> bios_;
|
||||||
std::vector<uint8_t> cartridge_;
|
std::vector<uint8_t> cartridge_;
|
||||||
@ -309,6 +356,11 @@ class ConcreteMachine:
|
|||||||
uint8_t ram_[1024];
|
uint8_t ram_[1024];
|
||||||
bool is_megacart_ = false;
|
bool is_megacart_ = false;
|
||||||
uint16_t cartridge_address_limit_ = 0;
|
uint16_t cartridge_address_limit_ = 0;
|
||||||
|
struct {
|
||||||
|
bool replace_bios = false;
|
||||||
|
bool replace_ram = false;
|
||||||
|
uint8_t ram[32768];
|
||||||
|
} super_game_module_;
|
||||||
|
|
||||||
std::vector<std::unique_ptr<Inputs::Joystick>> joysticks_;
|
std::vector<std::unique_ptr<Inputs::Joystick>> joysticks_;
|
||||||
bool joysticks_in_keypad_mode_ = false;
|
bool joysticks_in_keypad_mode_ = false;
|
||||||
|
@ -37,7 +37,7 @@ template <typename T, typename... R> class CompoundSource<T, R...>:
|
|||||||
CompoundSource(T &source, R &...next) : source_(source), next_source_(next...) {}
|
CompoundSource(T &source, R &...next) : source_(source), next_source_(next...) {}
|
||||||
|
|
||||||
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) {
|
||||||
if(source_.is_silent()) {
|
if(source_.is_zero_level()) {
|
||||||
source_.skip_samples(number_of_samples);
|
source_.skip_samples(number_of_samples);
|
||||||
next_source_.get_samples(number_of_samples, target);
|
next_source_.get_samples(number_of_samples, target);
|
||||||
} else {
|
} else {
|
||||||
|
@ -43,7 +43,7 @@ class SampleSource {
|
|||||||
fill the target with zeroes; @c false if a call might return all zeroes or
|
fill the target with zeroes; @c false if a call might return all zeroes or
|
||||||
might not.
|
might not.
|
||||||
*/
|
*/
|
||||||
bool is_silent() {
|
bool is_zero_level() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user