diff --git a/Machines/AmstradCPC/AmstradCPC.hpp b/Machines/AmstradCPC/AmstradCPC.hpp index 91c29e69f..2c7c6e089 100644 --- a/Machines/AmstradCPC/AmstradCPC.hpp +++ b/Machines/AmstradCPC/AmstradCPC.hpp @@ -45,7 +45,7 @@ enum Key: uint16_t { }; /*! - Models an Amstrad CPC, a CRT-outputting machine with a keyboard that can accept configuration targets. + Models an Amstrad CPC. */ class Machine: public CRTMachine::Machine, @@ -54,7 +54,7 @@ class Machine: public: virtual ~Machine(); - /// Creates an returns an Amstrad CPC on the heap. + /// Creates and returns an Amstrad CPC on the heap. static Machine *AmstradCPC(); /// Sets the contents of rom @c type to @c data. Assumed to be a setup step; has no effect once a machine is running. diff --git a/Machines/Atari2600/Atari2600.cpp b/Machines/Atari2600/Atari2600.cpp index c640dc91b..29e44e8c0 100644 --- a/Machines/Atari2600/Atari2600.cpp +++ b/Machines/Atari2600/Atari2600.cpp @@ -23,135 +23,183 @@ #include "Cartridges/Tigervision.hpp" #include "Cartridges/Unpaged.hpp" -using namespace Atari2600; namespace { static const double NTSC_clock_rate = 1194720; static const double PAL_clock_rate = 1182298; } -Machine::Machine() : - frame_record_pointer_(0), - is_ntsc_(true) { - set_clock_rate(NTSC_clock_rate); -} +namespace Atari2600 { -void Machine::setup_output(float aspect_ratio) { - bus_->tia_.reset(new TIA); - bus_->speaker_.reset(new Speaker); - bus_->speaker_->set_input_rate((float)(get_clock_rate() / (double)CPUTicksPerAudioTick)); - bus_->tia_->get_crt()->set_delegate(this); -} - -void Machine::close_output() { - bus_.reset(); -} - -Machine::~Machine() { - close_output(); -} - -void Machine::set_digital_input(Atari2600DigitalInput input, bool state) { - switch (input) { - case Atari2600DigitalInputJoy1Up: bus_->mos6532_.update_port_input(0, 0x10, state); break; - case Atari2600DigitalInputJoy1Down: bus_->mos6532_.update_port_input(0, 0x20, state); break; - case Atari2600DigitalInputJoy1Left: bus_->mos6532_.update_port_input(0, 0x40, state); break; - case Atari2600DigitalInputJoy1Right: bus_->mos6532_.update_port_input(0, 0x80, state); break; - - case Atari2600DigitalInputJoy2Up: bus_->mos6532_.update_port_input(0, 0x01, state); break; - case Atari2600DigitalInputJoy2Down: bus_->mos6532_.update_port_input(0, 0x02, state); break; - case Atari2600DigitalInputJoy2Left: bus_->mos6532_.update_port_input(0, 0x04, state); break; - case Atari2600DigitalInputJoy2Right: bus_->mos6532_.update_port_input(0, 0x08, state); break; - - // TODO: latching - case Atari2600DigitalInputJoy1Fire: if(state) bus_->tia_input_value_[0] &= ~0x80; else bus_->tia_input_value_[0] |= 0x80; break; - case Atari2600DigitalInputJoy2Fire: if(state) bus_->tia_input_value_[1] &= ~0x80; else bus_->tia_input_value_[1] |= 0x80; break; - - default: break; - } -} - -void Machine::set_switch_is_enabled(Atari2600Switch input, bool state) { - switch(input) { - case Atari2600SwitchReset: bus_->mos6532_.update_port_input(1, 0x01, state); break; - case Atari2600SwitchSelect: bus_->mos6532_.update_port_input(1, 0x02, state); break; - case Atari2600SwitchColour: bus_->mos6532_.update_port_input(1, 0x08, state); break; - case Atari2600SwitchLeftPlayerDifficulty: bus_->mos6532_.update_port_input(1, 0x40, state); break; - case Atari2600SwitchRightPlayerDifficulty: bus_->mos6532_.update_port_input(1, 0x80, state); break; - } -} - -void Machine::configure_as_target(const StaticAnalyser::Target &target) { - const std::vector &rom = target.cartridges.front()->get_segments().front().data; - switch(target.atari.paging_model) { - case StaticAnalyser::Atari2600PagingModel::ActivisionStack: bus_.reset(new Cartridge::Cartridge(rom)); break; - case StaticAnalyser::Atari2600PagingModel::CBSRamPlus: bus_.reset(new Cartridge::Cartridge(rom)); break; - case StaticAnalyser::Atari2600PagingModel::CommaVid: bus_.reset(new Cartridge::Cartridge(rom)); break; - case StaticAnalyser::Atari2600PagingModel::MegaBoy: bus_.reset(new Cartridge::Cartridge(rom)); break; - case StaticAnalyser::Atari2600PagingModel::MNetwork: bus_.reset(new Cartridge::Cartridge(rom)); break; - case StaticAnalyser::Atari2600PagingModel::None: bus_.reset(new Cartridge::Cartridge(rom)); break; - case StaticAnalyser::Atari2600PagingModel::ParkerBros: bus_.reset(new Cartridge::Cartridge(rom)); break; - case StaticAnalyser::Atari2600PagingModel::Pitfall2: bus_.reset(new Cartridge::Cartridge(rom)); break; - case StaticAnalyser::Atari2600PagingModel::Tigervision: bus_.reset(new Cartridge::Cartridge(rom)); break; - - case StaticAnalyser::Atari2600PagingModel::Atari8k: - if(target.atari.uses_superchip) { - bus_.reset(new Cartridge::Cartridge(rom)); - } else { - bus_.reset(new Cartridge::Cartridge(rom)); - } - break; - case StaticAnalyser::Atari2600PagingModel::Atari16k: - if(target.atari.uses_superchip) { - bus_.reset(new Cartridge::Cartridge(rom)); - } else { - bus_.reset(new Cartridge::Cartridge(rom)); - } - break; - case StaticAnalyser::Atari2600PagingModel::Atari32k: - if(target.atari.uses_superchip) { - bus_.reset(new Cartridge::Cartridge(rom)); - } else { - bus_.reset(new Cartridge::Cartridge(rom)); - } - break; - } -} - -#pragma mark - CRT delegate - -void Machine::crt_did_end_batch_of_frames(Outputs::CRT::CRT *crt, unsigned int number_of_frames, unsigned int number_of_unexpected_vertical_syncs) { - const size_t number_of_frame_records = sizeof(frame_records_) / sizeof(frame_records_[0]); - frame_records_[frame_record_pointer_ % number_of_frame_records].number_of_frames = number_of_frames; - frame_records_[frame_record_pointer_ % number_of_frame_records].number_of_unexpected_vertical_syncs = number_of_unexpected_vertical_syncs; - frame_record_pointer_ ++; - - if(frame_record_pointer_ >= 6) { - unsigned int total_number_of_frames = 0; - unsigned int total_number_of_unexpected_vertical_syncs = 0; - for(size_t c = 0; c < number_of_frame_records; c++) { - total_number_of_frames += frame_records_[c].number_of_frames; - total_number_of_unexpected_vertical_syncs += frame_records_[c].number_of_unexpected_vertical_syncs; +class ConcreteMachine: + public Machine, + public Outputs::CRT::Delegate { + public: + ConcreteMachine() : + frame_record_pointer_(0), + is_ntsc_(true) { + set_clock_rate(NTSC_clock_rate); } - if(total_number_of_unexpected_vertical_syncs >= total_number_of_frames >> 1) { - for(size_t c = 0; c < number_of_frame_records; c++) { - frame_records_[c].number_of_frames = 0; - frame_records_[c].number_of_unexpected_vertical_syncs = 0; - } - is_ntsc_ ^= true; - - double clock_rate; - if(is_ntsc_) { - clock_rate = NTSC_clock_rate; - bus_->tia_->set_output_mode(TIA::OutputMode::NTSC); - } else { - clock_rate = PAL_clock_rate; - bus_->tia_->set_output_mode(TIA::OutputMode::PAL); - } - - bus_->speaker_->set_input_rate((float)(clock_rate / (double)CPUTicksPerAudioTick)); - bus_->speaker_->set_high_frequency_cut_off((float)(clock_rate / ((double)CPUTicksPerAudioTick * 2.0))); - set_clock_rate(clock_rate); + ~ConcreteMachine() { + close_output(); } - } + + void configure_as_target(const StaticAnalyser::Target &target) { + const std::vector &rom = target.cartridges.front()->get_segments().front().data; + switch(target.atari.paging_model) { + case StaticAnalyser::Atari2600PagingModel::ActivisionStack: bus_.reset(new Cartridge::Cartridge(rom)); break; + case StaticAnalyser::Atari2600PagingModel::CBSRamPlus: bus_.reset(new Cartridge::Cartridge(rom)); break; + case StaticAnalyser::Atari2600PagingModel::CommaVid: bus_.reset(new Cartridge::Cartridge(rom)); break; + case StaticAnalyser::Atari2600PagingModel::MegaBoy: bus_.reset(new Cartridge::Cartridge(rom)); break; + case StaticAnalyser::Atari2600PagingModel::MNetwork: bus_.reset(new Cartridge::Cartridge(rom)); break; + case StaticAnalyser::Atari2600PagingModel::None: bus_.reset(new Cartridge::Cartridge(rom)); break; + case StaticAnalyser::Atari2600PagingModel::ParkerBros: bus_.reset(new Cartridge::Cartridge(rom)); break; + case StaticAnalyser::Atari2600PagingModel::Pitfall2: bus_.reset(new Cartridge::Cartridge(rom)); break; + case StaticAnalyser::Atari2600PagingModel::Tigervision: bus_.reset(new Cartridge::Cartridge(rom)); break; + + case StaticAnalyser::Atari2600PagingModel::Atari8k: + if(target.atari.uses_superchip) { + bus_.reset(new Cartridge::Cartridge(rom)); + } else { + bus_.reset(new Cartridge::Cartridge(rom)); + } + break; + case StaticAnalyser::Atari2600PagingModel::Atari16k: + if(target.atari.uses_superchip) { + bus_.reset(new Cartridge::Cartridge(rom)); + } else { + bus_.reset(new Cartridge::Cartridge(rom)); + } + break; + case StaticAnalyser::Atari2600PagingModel::Atari32k: + if(target.atari.uses_superchip) { + bus_.reset(new Cartridge::Cartridge(rom)); + } else { + bus_.reset(new Cartridge::Cartridge(rom)); + } + break; + } + } + + void set_digital_input(Atari2600DigitalInput input, bool state) { + switch (input) { + case Atari2600DigitalInputJoy1Up: bus_->mos6532_.update_port_input(0, 0x10, state); break; + case Atari2600DigitalInputJoy1Down: bus_->mos6532_.update_port_input(0, 0x20, state); break; + case Atari2600DigitalInputJoy1Left: bus_->mos6532_.update_port_input(0, 0x40, state); break; + case Atari2600DigitalInputJoy1Right: bus_->mos6532_.update_port_input(0, 0x80, state); break; + + case Atari2600DigitalInputJoy2Up: bus_->mos6532_.update_port_input(0, 0x01, state); break; + case Atari2600DigitalInputJoy2Down: bus_->mos6532_.update_port_input(0, 0x02, state); break; + case Atari2600DigitalInputJoy2Left: bus_->mos6532_.update_port_input(0, 0x04, state); break; + case Atari2600DigitalInputJoy2Right: bus_->mos6532_.update_port_input(0, 0x08, state); break; + + // TODO: latching + case Atari2600DigitalInputJoy1Fire: if(state) bus_->tia_input_value_[0] &= ~0x80; else bus_->tia_input_value_[0] |= 0x80; break; + case Atari2600DigitalInputJoy2Fire: if(state) bus_->tia_input_value_[1] &= ~0x80; else bus_->tia_input_value_[1] |= 0x80; break; + + default: break; + } + } + + + void set_switch_is_enabled(Atari2600Switch input, bool state) { + switch(input) { + case Atari2600SwitchReset: bus_->mos6532_.update_port_input(1, 0x01, state); break; + case Atari2600SwitchSelect: bus_->mos6532_.update_port_input(1, 0x02, state); break; + case Atari2600SwitchColour: bus_->mos6532_.update_port_input(1, 0x08, state); break; + case Atari2600SwitchLeftPlayerDifficulty: bus_->mos6532_.update_port_input(1, 0x40, state); break; + case Atari2600SwitchRightPlayerDifficulty: bus_->mos6532_.update_port_input(1, 0x80, state); break; + } + } + + void set_reset_switch(bool state) { + bus_->set_reset_line(state); + } + + // to satisfy CRTMachine::Machine + void setup_output(float aspect_ratio) { + bus_->tia_.reset(new TIA); + bus_->speaker_.reset(new Speaker); + bus_->speaker_->set_input_rate((float)(get_clock_rate() / (double)CPUTicksPerAudioTick)); + bus_->tia_->get_crt()->set_delegate(this); + } + + void close_output() { + bus_.reset(); + } + + std::shared_ptr get_crt() { + return bus_->tia_->get_crt(); + } + + std::shared_ptr get_speaker() { + return bus_->speaker_; + } + + void run_for(const Cycles cycles) { + bus_->run_for(cycles); + } + + // to satisfy Outputs::CRT::Delegate + void crt_did_end_batch_of_frames(Outputs::CRT::CRT *crt, unsigned int number_of_frames, unsigned int number_of_unexpected_vertical_syncs) { + const size_t number_of_frame_records = sizeof(frame_records_) / sizeof(frame_records_[0]); + frame_records_[frame_record_pointer_ % number_of_frame_records].number_of_frames = number_of_frames; + frame_records_[frame_record_pointer_ % number_of_frame_records].number_of_unexpected_vertical_syncs = number_of_unexpected_vertical_syncs; + frame_record_pointer_ ++; + + if(frame_record_pointer_ >= 6) { + unsigned int total_number_of_frames = 0; + unsigned int total_number_of_unexpected_vertical_syncs = 0; + for(size_t c = 0; c < number_of_frame_records; c++) { + total_number_of_frames += frame_records_[c].number_of_frames; + total_number_of_unexpected_vertical_syncs += frame_records_[c].number_of_unexpected_vertical_syncs; + } + + if(total_number_of_unexpected_vertical_syncs >= total_number_of_frames >> 1) { + for(size_t c = 0; c < number_of_frame_records; c++) { + frame_records_[c].number_of_frames = 0; + frame_records_[c].number_of_unexpected_vertical_syncs = 0; + } + is_ntsc_ ^= true; + + double clock_rate; + if(is_ntsc_) { + clock_rate = NTSC_clock_rate; + bus_->tia_->set_output_mode(TIA::OutputMode::NTSC); + } else { + clock_rate = PAL_clock_rate; + bus_->tia_->set_output_mode(TIA::OutputMode::PAL); + } + + bus_->speaker_->set_input_rate((float)(clock_rate / (double)CPUTicksPerAudioTick)); + bus_->speaker_->set_high_frequency_cut_off((float)(clock_rate / ((double)CPUTicksPerAudioTick * 2.0))); + set_clock_rate(clock_rate); + } + } + } + + + private: + // the bus + std::unique_ptr bus_; + + // output frame rate tracker + struct FrameRecord { + unsigned int number_of_frames; + unsigned int number_of_unexpected_vertical_syncs; + + FrameRecord() : number_of_frames(0), number_of_unexpected_vertical_syncs(0) {} + } frame_records_[4]; + unsigned int frame_record_pointer_; + bool is_ntsc_; +}; + } + +using namespace Atari2600; + +Machine *Machine::Atari2600() { + return new Atari2600::ConcreteMachine; +} + +Machine::~Machine() {} diff --git a/Machines/Atari2600/Atari2600.hpp b/Machines/Atari2600/Atari2600.hpp index 5fa36993e..860773d23 100644 --- a/Machines/Atari2600/Atari2600.hpp +++ b/Machines/Atari2600/Atari2600.hpp @@ -9,59 +9,33 @@ #ifndef Atari2600_cpp #define Atari2600_cpp -#include - -#include "../../Processors/6502/6502.hpp" -#include "../CRTMachine.hpp" -#include "Bus.hpp" -#include "PIA.hpp" -#include "Speaker.hpp" -#include "TIA.hpp" - #include "../ConfigurationTarget.hpp" +#include "../CRTMachine.hpp" + #include "Atari2600Inputs.h" namespace Atari2600 { +/*! + Models an Atari 2600. +*/ class Machine: public CRTMachine::Machine, - public ConfigurationTarget::Machine, - public Outputs::CRT::Delegate { - + public ConfigurationTarget::Machine { public: - Machine(); - ~Machine(); + virtual ~Machine(); - void configure_as_target(const StaticAnalyser::Target &target); - void switch_region(); + /// Creates and returns an Atari 2600 on the heap. + static Machine *Atari2600(); - void set_digital_input(Atari2600DigitalInput input, bool state); - void set_switch_is_enabled(Atari2600Switch input, bool state); - void set_reset_line(bool state) { bus_->set_reset_line(state); } + /// Sets @c input to @c state. + virtual void set_digital_input(Atari2600DigitalInput input, bool state) = 0; - // to satisfy CRTMachine::Machine - virtual void setup_output(float aspect_ratio); - virtual void close_output(); - virtual std::shared_ptr get_crt() { return bus_->tia_->get_crt(); } - virtual std::shared_ptr get_speaker() { return bus_->speaker_; } - virtual void run_for(const Cycles cycles) { bus_->run_for(cycles); } + /// Sets the switch @c input to @c state. + virtual void set_switch_is_enabled(Atari2600Switch input, bool state) = 0; - // to satisfy Outputs::CRT::Delegate - virtual void crt_did_end_batch_of_frames(Outputs::CRT::CRT *crt, unsigned int number_of_frames, unsigned int number_of_unexpected_vertical_syncs); - - private: - // the bus - std::unique_ptr bus_; - - // output frame rate tracker - struct FrameRecord { - unsigned int number_of_frames; - unsigned int number_of_unexpected_vertical_syncs; - - FrameRecord() : number_of_frames(0), number_of_unexpected_vertical_syncs(0) {} - } frame_records_[4]; - unsigned int frame_record_pointer_; - bool is_ntsc_; + // Presses or releases the reset button. + virtual void set_reset_switch(bool state) = 0; }; } diff --git a/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSAtari2600.mm b/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSAtari2600.mm index 08c003fbc..07ef20dc4 100644 --- a/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSAtari2600.mm +++ b/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSAtari2600.mm @@ -12,7 +12,14 @@ #import "CSMachine+Subclassing.h" @implementation CSAtari2600 { - Atari2600::Machine _atari2600; + std::unique_ptr _atari2600; +} + +- (CRTMachine::Machine * const)machine { + if(!_atari2600) { + _atari2600.reset(Atari2600::Machine::Atari2600()); + } + return _atari2600.get(); } - (void)setDirection:(CSJoystickDirection)direction onPad:(NSUInteger)pad isPressed:(BOOL)isPressed { @@ -25,19 +32,19 @@ case CSJoystickDirectionRight: input = pad ? Atari2600DigitalInputJoy2Right : Atari2600DigitalInputJoy1Right; break; } @synchronized(self) { - _atari2600.set_digital_input(input, isPressed ? true : false); + _atari2600->set_digital_input(input, isPressed ? true : false); } } - (void)setButtonAtIndex:(NSUInteger)button onPad:(NSUInteger)pad isPressed:(BOOL)isPressed { @synchronized(self) { - _atari2600.set_digital_input(pad ? Atari2600DigitalInputJoy2Fire : Atari2600DigitalInputJoy1Fire, isPressed ? true : false); + _atari2600->set_digital_input(pad ? Atari2600DigitalInputJoy2Fire : Atari2600DigitalInputJoy1Fire, isPressed ? true : false); } } - (void)setResetLineEnabled:(BOOL)enabled { @synchronized(self) { - _atari2600.set_reset_line(enabled ? true : false); + _atari2600->set_reset_switch(enabled ? true : false); } } @@ -47,40 +54,36 @@ } } -- (CRTMachine::Machine * const)machine { - return &_atari2600; -} - #pragma mark - Switches - (void)setColourButton:(BOOL)colourButton { _colourButton = colourButton; @synchronized(self) { - _atari2600.set_switch_is_enabled(Atari2600SwitchColour, colourButton); + _atari2600->set_switch_is_enabled(Atari2600SwitchColour, colourButton); } } - (void)setLeftPlayerDifficultyButton:(BOOL)leftPlayerDifficultyButton { _leftPlayerDifficultyButton = leftPlayerDifficultyButton; @synchronized(self) { - _atari2600.set_switch_is_enabled(Atari2600SwitchLeftPlayerDifficulty, leftPlayerDifficultyButton); + _atari2600->set_switch_is_enabled(Atari2600SwitchLeftPlayerDifficulty, leftPlayerDifficultyButton); } } - (void)setRightPlayerDifficultyButton:(BOOL)rightPlayerDifficultyButton { _rightPlayerDifficultyButton = rightPlayerDifficultyButton; @synchronized(self) { - _atari2600.set_switch_is_enabled(Atari2600SwitchRightPlayerDifficulty, rightPlayerDifficultyButton); + _atari2600->set_switch_is_enabled(Atari2600SwitchRightPlayerDifficulty, rightPlayerDifficultyButton); } } - (void)toggleSwitch:(Atari2600Switch)toggleSwitch { @synchronized(self) { - _atari2600.set_switch_is_enabled(toggleSwitch, true); + _atari2600->set_switch_is_enabled(toggleSwitch, true); } dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ @synchronized(self) { - _atari2600.set_switch_is_enabled(toggleSwitch, false); + _atari2600->set_switch_is_enabled(toggleSwitch, false); } }); }