diff --git a/Activity/Observer.hpp b/Activity/Observer.hpp index 346219b70..4ebcd11ec 100644 --- a/Activity/Observer.hpp +++ b/Activity/Observer.hpp @@ -22,38 +22,38 @@ namespace Activity { and/or to show or unshow status indicators. */ class Observer { - public: - virtual ~Observer() = default; +public: + virtual ~Observer() = default; - /// Provides hints as to the sort of information presented on an LED. - enum LEDPresentation: uint8_t { - /// This LED informs the user of some sort of persistent state, e.g. scroll lock. - /// If this flag is absent then the LED describes an ephemeral state, such as media access. - Persistent = (1 << 0), - }; + /// Provides hints as to the sort of information presented on an LED. + enum LEDPresentation: uint8_t { + /// This LED informs the user of some sort of persistent state, e.g. scroll lock. + /// If this flag is absent then the LED describes an ephemeral state, such as media access. + Persistent = (1 << 0), + }; - /// Announces to the receiver that there is an LED of name @c name. - virtual void register_led([[maybe_unused]] const std::string &name, [[maybe_unused]] uint8_t presentation = 0) {} + /// Announces to the receiver that there is an LED of name @c name. + virtual void register_led([[maybe_unused]] const std::string &name, [[maybe_unused]] uint8_t presentation = 0) {} - /// Announces to the receiver that there is a drive of name @c name. - /// - /// If a drive has the same name as an LED, that LED goes with this drive. - virtual void register_drive([[maybe_unused]] const std::string &name) {} + /// Announces to the receiver that there is a drive of name @c name. + /// + /// If a drive has the same name as an LED, that LED goes with this drive. + virtual void register_drive([[maybe_unused]] const std::string &name) {} - /// Informs the receiver of the new state of the LED with name @c name. - virtual void set_led_status([[maybe_unused]] const std::string &name, [[maybe_unused]] bool lit) {} + /// Informs the receiver of the new state of the LED with name @c name. + virtual void set_led_status([[maybe_unused]] const std::string &name, [[maybe_unused]] bool lit) {} - enum class DriveEvent { - StepNormal, - StepBelowZero, - StepBeyondMaximum - }; + enum class DriveEvent { + StepNormal, + StepBelowZero, + StepBeyondMaximum + }; - /// Informs the receiver that the named event just occurred for the drive with name @c name. - virtual void announce_drive_event([[maybe_unused]] const std::string &name, [[maybe_unused]] DriveEvent event) {} + /// Informs the receiver that the named event just occurred for the drive with name @c name. + virtual void announce_drive_event([[maybe_unused]] const std::string &name, [[maybe_unused]] DriveEvent event) {} - /// Informs the receiver of the motor-on status of the drive with name @c name. - virtual void set_drive_motor_status([[maybe_unused]] const std::string &name, [[maybe_unused]] bool is_on) {} + /// Informs the receiver of the motor-on status of the drive with name @c name. + virtual void set_drive_motor_status([[maybe_unused]] const std::string &name, [[maybe_unused]] bool is_on) {} }; } diff --git a/Activity/Source.hpp b/Activity/Source.hpp index 6f540cf33..86d9642e4 100644 --- a/Activity/Source.hpp +++ b/Activity/Source.hpp @@ -13,8 +13,8 @@ namespace Activity { class Source { - public: - virtual void set_activity_observer(Observer *observer) = 0; +public: + virtual void set_activity_observer(Observer *observer) = 0; }; } diff --git a/Analyser/Dynamic/ConfidenceCounter.hpp b/Analyser/Dynamic/ConfidenceCounter.hpp index 8b8e80ea2..ca1002748 100644 --- a/Analyser/Dynamic/ConfidenceCounter.hpp +++ b/Analyser/Dynamic/ConfidenceCounter.hpp @@ -18,25 +18,25 @@ namespace Analyser::Dynamic { The initial value of the confidence counter is 0.5. */ class ConfidenceCounter: public ConfidenceSource { - public: - /*! @returns The computed probability, based on the history of events. */ - float get_confidence() final; +public: + /*! @returns The computed probability, based on the history of events. */ + float get_confidence() final; - /*! Records an event that implies this is the appropriate class: pushes probability up towards 1.0. */ - void add_hit(); + /*! Records an event that implies this is the appropriate class: pushes probability up towards 1.0. */ + void add_hit(); - /*! Records an event that implies this is not the appropriate class: pushes probability down towards 0.0. */ - void add_miss(); + /*! Records an event that implies this is not the appropriate class: pushes probability down towards 0.0. */ + void add_miss(); - /*! - Records an event that could be correct but isn't necessarily so; which can push probability - down towards 0.5, but will never push it upwards. - */ - void add_equivocal(); + /*! + Records an event that could be correct but isn't necessarily so; which can push probability + down towards 0.5, but will never push it upwards. + */ + void add_equivocal(); - private: - int hits_ = 1; - int misses_ = 1; +private: + int hits_ = 1; + int misses_ = 1; }; } diff --git a/Analyser/Dynamic/ConfidenceSummary.cpp b/Analyser/Dynamic/ConfidenceSummary.cpp index 5ffd9264f..c3f0c2607 100644 --- a/Analyser/Dynamic/ConfidenceSummary.cpp +++ b/Analyser/Dynamic/ConfidenceSummary.cpp @@ -13,7 +13,10 @@ using namespace Analyser::Dynamic; -ConfidenceSummary::ConfidenceSummary(const std::vector &sources, const std::vector &weights) : +ConfidenceSummary::ConfidenceSummary( + const std::vector &sources, + const std::vector &weights +) : sources_(sources), weights_(weights) { assert(weights.size() == sources.size()); weight_sum_ = std::accumulate(weights.begin(), weights.end(), 0.0f); diff --git a/Analyser/Dynamic/ConfidenceSummary.hpp b/Analyser/Dynamic/ConfidenceSummary.hpp index 880a9e8ea..90f3e78c9 100644 --- a/Analyser/Dynamic/ConfidenceSummary.hpp +++ b/Analyser/Dynamic/ConfidenceSummary.hpp @@ -18,24 +18,24 @@ namespace Analyser::Dynamic { Summaries a collection of confidence sources by calculating their weighted sum. */ class ConfidenceSummary: public ConfidenceSource { - public: - /*! - Instantiates a summary that will produce the weighted sum of - @c sources, each using the corresponding entry of @c weights. +public: + /*! + Instantiates a summary that will produce the weighted sum of + @c sources, each using the corresponding entry of @c weights. - Requires that @c sources and @c weights are of the same length. - */ - ConfidenceSummary( - const std::vector &sources, - const std::vector &weights); + Requires that @c sources and @c weights are of the same length. + */ + ConfidenceSummary( + const std::vector &sources, + const std::vector &weights); - /*! @returns The weighted sum of all sources. */ - float get_confidence() final; + /*! @returns The weighted sum of all sources. */ + float get_confidence() final; - private: - const std::vector sources_; - const std::vector weights_; - float weight_sum_; +private: + const std::vector sources_; + const std::vector weights_; + float weight_sum_; }; } diff --git a/Analyser/Dynamic/MultiMachine/Implementation/MultiConfigurable.cpp b/Analyser/Dynamic/MultiMachine/Implementation/MultiConfigurable.cpp index 28dbd9175..3556719b1 100644 --- a/Analyser/Dynamic/MultiMachine/Implementation/MultiConfigurable.cpp +++ b/Analyser/Dynamic/MultiMachine/Implementation/MultiConfigurable.cpp @@ -15,90 +15,90 @@ using namespace Analyser::Dynamic; namespace { class MultiStruct: public Reflection::Struct { - public: - MultiStruct(const std::vector &devices) : devices_(devices) { - for(auto device: devices) { - options_.emplace_back(device->get_options()); +public: + MultiStruct(const std::vector &devices) : devices_(devices) { + for(auto device: devices) { + options_.emplace_back(device->get_options()); + } + } + + void apply() { + auto options = options_.begin(); + for(auto device: devices_) { + device->set_options(*options); + ++options; + } + } + + std::vector all_keys() const final { + std::set keys; + for(auto &options: options_) { + const auto new_keys = options->all_keys(); + keys.insert(new_keys.begin(), new_keys.end()); + } + return std::vector(keys.begin(), keys.end()); + } + + std::vector values_for(const std::string &name) const final { + std::set values; + for(auto &options: options_) { + const auto new_values = options->values_for(name); + values.insert(new_values.begin(), new_values.end()); + } + return std::vector(values.begin(), values.end()); + } + + const std::type_info *type_of(const std::string &name) const final { + for(auto &options: options_) { + auto info = options->type_of(name); + if(info) return info; + } + return nullptr; + } + + size_t count_of(const std::string &name) const final { + for(auto &options: options_) { + auto info = options->type_of(name); + if(info) return options->count_of(name); + } + return 0; + } + + const void *get(const std::string &name) const final { + for(auto &options: options_) { + auto value = options->get(name); + if(value) return value; + } + return nullptr; + } + + void *get(const std::string &name) final { + for(auto &options: options_) { + auto value = options->get(name); + if(value) return value; + } + return nullptr; + } + + void set(const std::string &name, const void *value, const size_t offset) final { + const auto safe_type = type_of(name); + if(!safe_type) return; + + // Set this property only where the child's type is the same as that + // which was returned from here for type_of. + for(auto &options: options_) { + const auto type = options->type_of(name); + if(!type) continue; + + if(*type == *safe_type) { + options->set(name, value, offset); } } + } - void apply() { - auto options = options_.begin(); - for(auto device: devices_) { - device->set_options(*options); - ++options; - } - } - - std::vector all_keys() const final { - std::set keys; - for(auto &options: options_) { - const auto new_keys = options->all_keys(); - keys.insert(new_keys.begin(), new_keys.end()); - } - return std::vector(keys.begin(), keys.end()); - } - - std::vector values_for(const std::string &name) const final { - std::set values; - for(auto &options: options_) { - const auto new_values = options->values_for(name); - values.insert(new_values.begin(), new_values.end()); - } - return std::vector(values.begin(), values.end()); - } - - const std::type_info *type_of(const std::string &name) const final { - for(auto &options: options_) { - auto info = options->type_of(name); - if(info) return info; - } - return nullptr; - } - - size_t count_of(const std::string &name) const final { - for(auto &options: options_) { - auto info = options->type_of(name); - if(info) return options->count_of(name); - } - return 0; - } - - const void *get(const std::string &name) const final { - for(auto &options: options_) { - auto value = options->get(name); - if(value) return value; - } - return nullptr; - } - - void *get(const std::string &name) final { - for(auto &options: options_) { - auto value = options->get(name); - if(value) return value; - } - return nullptr; - } - - void set(const std::string &name, const void *value, size_t offset) final { - const auto safe_type = type_of(name); - if(!safe_type) return; - - // Set this property only where the child's type is the same as that - // which was returned from here for type_of. - for(auto &options: options_) { - const auto type = options->type_of(name); - if(!type) continue; - - if(*type == *safe_type) { - options->set(name, value, offset); - } - } - } - - private: - const std::vector &devices_; - std::vector> options_; +private: + const std::vector &devices_; + std::vector> options_; }; } diff --git a/Analyser/Dynamic/MultiMachine/Implementation/MultiConfigurable.hpp b/Analyser/Dynamic/MultiMachine/Implementation/MultiConfigurable.hpp index c18e039f0..07da52d3d 100644 --- a/Analyser/Dynamic/MultiMachine/Implementation/MultiConfigurable.hpp +++ b/Analyser/Dynamic/MultiMachine/Implementation/MultiConfigurable.hpp @@ -23,15 +23,15 @@ namespace Analyser::Dynamic { order of delivered messages. */ class MultiConfigurable: public Configurable::Device { - public: - MultiConfigurable(const std::vector> &machines); +public: + MultiConfigurable(const std::vector> &); - // Below is the standard Configurable::Device interface; see there for documentation. - void set_options(const std::unique_ptr &options) final; - std::unique_ptr get_options() final; + // Below is the standard Configurable::Device interface; see there for documentation. + void set_options(const std::unique_ptr &) final; + std::unique_ptr get_options() final; - private: - std::vector devices_; +private: + std::vector devices_; }; } diff --git a/Analyser/Dynamic/MultiMachine/Implementation/MultiJoystickMachine.cpp b/Analyser/Dynamic/MultiMachine/Implementation/MultiJoystickMachine.cpp index 7bf109e1b..4d3cf7404 100644 --- a/Analyser/Dynamic/MultiMachine/Implementation/MultiJoystickMachine.cpp +++ b/Analyser/Dynamic/MultiMachine/Implementation/MultiJoystickMachine.cpp @@ -15,52 +15,52 @@ using namespace Analyser::Dynamic; namespace { class MultiJoystick: public Inputs::Joystick { - public: - MultiJoystick(std::vector &machines, std::size_t index) { - for(const auto &machine: machines) { - const auto &joysticks = machine->get_joysticks(); - if(joysticks.size() >= index) { - joysticks_.push_back(joysticks[index].get()); - } +public: + MultiJoystick(std::vector &machines, const std::size_t index) { + for(const auto &machine: machines) { + const auto &joysticks = machine->get_joysticks(); + if(joysticks.size() >= index) { + joysticks_.push_back(joysticks[index].get()); } } + } - const std::vector &get_inputs() final { - if(inputs.empty()) { - for(const auto &joystick: joysticks_) { - std::vector joystick_inputs = joystick->get_inputs(); - for(const auto &input: joystick_inputs) { - if(std::find(inputs.begin(), inputs.end(), input) != inputs.end()) { - inputs.push_back(input); - } + const std::vector &get_inputs() final { + if(inputs.empty()) { + for(const auto &joystick: joysticks_) { + std::vector joystick_inputs = joystick->get_inputs(); + for(const auto &input: joystick_inputs) { + if(std::find(inputs.begin(), inputs.end(), input) != inputs.end()) { + inputs.push_back(input); } } } - - return inputs; } - void set_input(const Input &digital_input, bool is_active) final { - for(const auto &joystick: joysticks_) { - joystick->set_input(digital_input, is_active); - } - } + return inputs; + } - void set_input(const Input &digital_input, float value) final { - for(const auto &joystick: joysticks_) { - joystick->set_input(digital_input, value); - } + void set_input(const Input &digital_input, const bool is_active) final { + for(const auto &joystick: joysticks_) { + joystick->set_input(digital_input, is_active); } + } - void reset_all_inputs() final { - for(const auto &joystick: joysticks_) { - joystick->reset_all_inputs(); - } + void set_input(const Input &digital_input, const float value) final { + for(const auto &joystick: joysticks_) { + joystick->set_input(digital_input, value); } + } - private: - std::vector inputs; - std::vector joysticks_; + void reset_all_inputs() final { + for(const auto &joystick: joysticks_) { + joystick->reset_all_inputs(); + } + } + +private: + std::vector inputs; + std::vector joysticks_; }; } diff --git a/Analyser/Dynamic/MultiMachine/Implementation/MultiJoystickMachine.hpp b/Analyser/Dynamic/MultiMachine/Implementation/MultiJoystickMachine.hpp index 85fe40091..6a68fb2b5 100644 --- a/Analyser/Dynamic/MultiMachine/Implementation/MultiJoystickMachine.hpp +++ b/Analyser/Dynamic/MultiMachine/Implementation/MultiJoystickMachine.hpp @@ -22,14 +22,14 @@ namespace Analyser::Dynamic { order of delivered messages. */ class MultiJoystickMachine: public MachineTypes::JoystickMachine { - public: - MultiJoystickMachine(const std::vector> &machines); +public: + MultiJoystickMachine(const std::vector> &); - // Below is the standard JoystickMachine::Machine interface; see there for documentation. - const std::vector> &get_joysticks() final; + // Below is the standard JoystickMachine::Machine interface; see there for documentation. + const std::vector> &get_joysticks() final; - private: - std::vector> joysticks_; +private: + std::vector> joysticks_; }; } diff --git a/Analyser/Dynamic/MultiMachine/Implementation/MultiKeyboardMachine.cpp b/Analyser/Dynamic/MultiMachine/Implementation/MultiKeyboardMachine.cpp index d5a42ba5b..bb5cd0ce1 100644 --- a/Analyser/Dynamic/MultiMachine/Implementation/MultiKeyboardMachine.cpp +++ b/Analyser/Dynamic/MultiMachine/Implementation/MultiKeyboardMachine.cpp @@ -24,7 +24,7 @@ void MultiKeyboardMachine::clear_all_keys() { } } -void MultiKeyboardMachine::set_key_state(uint16_t key, bool is_pressed) { +void MultiKeyboardMachine::set_key_state(const uint16_t key, const bool is_pressed) { for(const auto &machine: machines_) { machine->set_key_state(key, is_pressed); } @@ -36,7 +36,7 @@ void MultiKeyboardMachine::type_string(const std::string &string) { } } -bool MultiKeyboardMachine::can_type(char c) const { +bool MultiKeyboardMachine::can_type(const char c) const { bool can_type = true; for(const auto &machine: machines_) { can_type &= machine->can_type(c); @@ -51,12 +51,20 @@ Inputs::Keyboard &MultiKeyboardMachine::get_keyboard() { MultiKeyboardMachine::MultiKeyboard::MultiKeyboard(const std::vector<::MachineTypes::KeyboardMachine *> &machines) : machines_(machines) { for(const auto &machine: machines_) { - observed_keys_.insert(machine->get_keyboard().observed_keys().begin(), machine->get_keyboard().observed_keys().end()); + observed_keys_.insert( + machine->get_keyboard().observed_keys().begin(), + machine->get_keyboard().observed_keys().end() + ); is_exclusive_ |= machine->get_keyboard().is_exclusive(); } } -bool MultiKeyboardMachine::MultiKeyboard::set_key_pressed(Key key, char value, bool is_pressed, bool is_repeat) { +bool MultiKeyboardMachine::MultiKeyboard::set_key_pressed( + const Key key, + const char value, + const bool is_pressed, + const bool is_repeat +) { bool was_consumed = false; for(const auto &machine: machines_) { was_consumed |= machine->get_keyboard().set_key_pressed(key, value, is_pressed, is_repeat); diff --git a/Analyser/Dynamic/MultiMachine/Implementation/MultiKeyboardMachine.hpp b/Analyser/Dynamic/MultiMachine/Implementation/MultiKeyboardMachine.hpp index 2bda83513..b04064020 100644 --- a/Analyser/Dynamic/MultiMachine/Implementation/MultiKeyboardMachine.hpp +++ b/Analyser/Dynamic/MultiMachine/Implementation/MultiKeyboardMachine.hpp @@ -23,34 +23,34 @@ namespace Analyser::Dynamic { order of delivered messages. */ class MultiKeyboardMachine: public MachineTypes::KeyboardMachine { - private: - std::vector machines_; +private: + std::vector machines_; - class MultiKeyboard: public Inputs::Keyboard { - public: - MultiKeyboard(const std::vector &machines); + class MultiKeyboard: public Inputs::Keyboard { + public: + MultiKeyboard(const std::vector &); - bool set_key_pressed(Key key, char value, bool is_pressed, bool is_repeat) final; - void reset_all_keys() final; - const std::set &observed_keys() const final; - bool is_exclusive() const final; + bool set_key_pressed(Key key, char value, bool is_pressed, bool is_repeat) final; + void reset_all_keys() final; + const std::set &observed_keys() const final; + bool is_exclusive() const final; - private: - const std::vector &machines_; - std::set observed_keys_; - bool is_exclusive_ = false; - }; - std::unique_ptr keyboard_; + private: + const std::vector &machines_; + std::set observed_keys_; + bool is_exclusive_ = false; + }; + std::unique_ptr keyboard_; - public: - MultiKeyboardMachine(const std::vector> &machines); +public: + MultiKeyboardMachine(const std::vector> &machines); - // Below is the standard KeyboardMachine::Machine interface; see there for documentation. - void clear_all_keys() final; - void set_key_state(uint16_t key, bool is_pressed) final; - void type_string(const std::string &) final; - bool can_type(char c) const final; - Inputs::Keyboard &get_keyboard() final; + // Below is the standard KeyboardMachine::Machine interface; see there for documentation. + void clear_all_keys() final; + void set_key_state(uint16_t key, bool is_pressed) final; + void type_string(const std::string &) final; + bool can_type(char c) const final; + Inputs::Keyboard &get_keyboard() final; }; } diff --git a/Analyser/Dynamic/MultiMachine/Implementation/MultiMediaTarget.hpp b/Analyser/Dynamic/MultiMachine/Implementation/MultiMediaTarget.hpp index 09a1f39d6..b1786441f 100644 --- a/Analyser/Dynamic/MultiMachine/Implementation/MultiMediaTarget.hpp +++ b/Analyser/Dynamic/MultiMachine/Implementation/MultiMediaTarget.hpp @@ -23,14 +23,14 @@ namespace Analyser::Dynamic { order of delivered messages. */ struct MultiMediaTarget: public MachineTypes::MediaTarget { - public: - MultiMediaTarget(const std::vector> &machines); +public: + MultiMediaTarget(const std::vector> &); - // Below is the standard MediaTarget::Machine interface; see there for documentation. - bool insert_media(const Analyser::Static::Media &media) final; + // Below is the standard MediaTarget::Machine interface; see there for documentation. + bool insert_media(const Analyser::Static::Media &) final; - private: - std::vector targets_; +private: + std::vector targets_; }; } diff --git a/Analyser/Dynamic/MultiMachine/Implementation/MultiProducer.cpp b/Analyser/Dynamic/MultiMachine/Implementation/MultiProducer.cpp index e9c614a83..c2c2c748a 100644 --- a/Analyser/Dynamic/MultiMachine/Implementation/MultiProducer.cpp +++ b/Analyser/Dynamic/MultiMachine/Implementation/MultiProducer.cpp @@ -53,7 +53,7 @@ void MultiInterface::perform_serial(const std::function> &machines, std::recursive_mutex &machines_mutex) : MultiInterface(machines, machines_mutex) { +MultiAudioProducer::MultiAudioProducer( + const std::vector> &machines, + std::recursive_mutex &machines_mutex +) : + MultiInterface(machines, machines_mutex) +{ speaker_ = MultiSpeaker::create(machines); } @@ -96,7 +101,7 @@ void MultiAudioProducer::did_change_machine_order() { // MARK: - MultiTimedMachine -void MultiTimedMachine::run_for(Time::Seconds duration) { +void MultiTimedMachine::run_for(const Time::Seconds duration) { perform_parallel([duration](::MachineTypes::TimedMachine *machine) { if(machine->get_confidence() >= 0.01f) machine->run_for(duration); }); diff --git a/Analyser/Dynamic/MultiMachine/Implementation/MultiProducer.hpp b/Analyser/Dynamic/MultiMachine/Implementation/MultiProducer.hpp index 35e153a84..a69291d00 100644 --- a/Analyser/Dynamic/MultiMachine/Implementation/MultiProducer.hpp +++ b/Analyser/Dynamic/MultiMachine/Implementation/MultiProducer.hpp @@ -21,88 +21,91 @@ namespace Analyser::Dynamic { template class MultiInterface { - public: - MultiInterface(const std::vector> &machines, std::recursive_mutex &machines_mutex) : - machines_(machines), machines_mutex_(machines_mutex), queues_(machines.size()) {} +public: + MultiInterface( + const std::vector> &machines, + std::recursive_mutex &machines_mutex + ) : + machines_(machines), machines_mutex_(machines_mutex), queues_(machines.size()) {} - protected: - /*! - Performs a parallel for operation across all machines, performing the supplied - function on each and returning only once all applications have completed. +protected: + /*! + Performs a parallel for operation across all machines, performing the supplied + function on each and returning only once all applications have completed. - No guarantees are extended as to which thread operations will occur on. - */ - void perform_parallel(const std::function &); + No guarantees are extended as to which thread operations will occur on. + */ + void perform_parallel(const std::function &); - /*! - Performs a serial for operation across all machines, performing the supplied - function on each on the calling thread. - */ - void perform_serial(const std::function &); + /*! + Performs a serial for operation across all machines, performing the supplied + function on each on the calling thread. + */ + void perform_serial(const std::function &); - protected: - const std::vector> &machines_; - std::recursive_mutex &machines_mutex_; +protected: + const std::vector> &machines_; + std::recursive_mutex &machines_mutex_; - private: - std::vector> queues_; +private: + std::vector> queues_; }; class MultiTimedMachine: public MultiInterface, public MachineTypes::TimedMachine { - public: - using MultiInterface::MultiInterface; +public: + using MultiInterface::MultiInterface; - /*! - Provides a mechanism by which a delegate can be informed each time a call to run_for has - been received. - */ - struct Delegate { - virtual void did_run_machines(MultiTimedMachine *) = 0; - }; - /// Sets @c delegate as the receiver of delegate messages. - void set_delegate(Delegate *delegate) { - delegate_ = delegate; - } + /*! + Provides a mechanism by which a delegate can be informed each time a call to run_for has + been received. + */ + struct Delegate { + virtual void did_run_machines(MultiTimedMachine *) = 0; + }; + /// Sets @c delegate as the receiver of delegate messages. + void set_delegate(Delegate *const delegate) { + delegate_ = delegate; + } - void run_for(Time::Seconds duration) final; + void run_for(Time::Seconds duration) final; - private: - void run_for(const Cycles) final {} - Delegate *delegate_ = nullptr; +private: + void run_for(Cycles) final {} + Delegate *delegate_ = nullptr; }; class MultiScanProducer: public MultiInterface, public MachineTypes::ScanProducer { - public: - using MultiInterface::MultiInterface; +public: + using MultiInterface::MultiInterface; - /*! - Informs the MultiScanProducer that the order of machines has changed; it - uses this as an opportunity to synthesis any CRTMachine::Machine::Delegate messages that - are necessary to bridge the gap between one machine and the next. - */ - void did_change_machine_order(); + /*! + Informs the MultiScanProducer that the order of machines has changed; it + uses this as an opportunity to synthesis any CRTMachine::Machine::Delegate messages that + are necessary to bridge the gap between one machine and the next. + */ + void did_change_machine_order(); - void set_scan_target(Outputs::Display::ScanTarget *scan_target) final; - Outputs::Display::ScanStatus get_scan_status() const final; + void set_scan_target(Outputs::Display::ScanTarget *) final; + Outputs::Display::ScanStatus get_scan_status() const final; - private: - Outputs::Display::ScanTarget *scan_target_ = nullptr; +private: + Outputs::Display::ScanTarget *scan_target_ = nullptr; }; class MultiAudioProducer: public MultiInterface, public MachineTypes::AudioProducer { - public: - MultiAudioProducer(const std::vector> &machines, std::recursive_mutex &machines_mutex); +public: + MultiAudioProducer(const std::vector> &, std::recursive_mutex &); - /*! - Informs the MultiAudio that the order of machines has changed; it - uses this as an opportunity to switch speaker delegates as appropriate. - */ - void did_change_machine_order(); + /*! + Informs the MultiAudio that the order of machines has changed; it + uses this as an opportunity to switch speaker delegates as appropriate. + */ + void did_change_machine_order(); - Outputs::Speaker::Speaker *get_speaker() final; + Outputs::Speaker::Speaker *get_speaker() final; - private: - MultiSpeaker *speaker_ = nullptr; +private: + MultiSpeaker *speaker_ = nullptr; }; /*! diff --git a/Analyser/Dynamic/MultiMachine/Implementation/MultiSpeaker.cpp b/Analyser/Dynamic/MultiMachine/Implementation/MultiSpeaker.cpp index 32882b9db..5bec5d698 100644 --- a/Analyser/Dynamic/MultiMachine/Implementation/MultiSpeaker.cpp +++ b/Analyser/Dynamic/MultiMachine/Implementation/MultiSpeaker.cpp @@ -28,7 +28,7 @@ MultiSpeaker::MultiSpeaker(const std::vector &speak } } -float MultiSpeaker::get_ideal_clock_rate_in_range(float minimum, float maximum) { +float MultiSpeaker::get_ideal_clock_rate_in_range(const float minimum, const float maximum) { float ideal = 0.0f; for(const auto &speaker: speakers_) { ideal += speaker->get_ideal_clock_rate_in_range(minimum, maximum); @@ -37,7 +37,7 @@ float MultiSpeaker::get_ideal_clock_rate_in_range(float minimum, float maximum) return ideal / float(speakers_.size()); } -void MultiSpeaker::set_computed_output_rate(float cycles_per_second, int buffer_size, bool stereo) { +void MultiSpeaker::set_computed_output_rate(const float cycles_per_second, const int buffer_size, const bool stereo) { stereo_output_ = stereo; for(const auto &speaker: speakers_) { speaker->set_computed_output_rate(cycles_per_second, buffer_size, stereo); @@ -54,13 +54,13 @@ bool MultiSpeaker::get_is_stereo() { return false; } -void MultiSpeaker::set_output_volume(float volume) { +void MultiSpeaker::set_output_volume(const float volume) { for(const auto &speaker: speakers_) { speaker->set_output_volume(volume); } } -void MultiSpeaker::speaker_did_complete_samples(Speaker *speaker, const std::vector &buffer) { +void MultiSpeaker::speaker_did_complete_samples(Speaker *const speaker, const std::vector &buffer) { auto delegate = delegate_.load(std::memory_order_relaxed); if(!delegate) return; { @@ -70,7 +70,7 @@ void MultiSpeaker::speaker_did_complete_samples(Speaker *speaker, const std::vec did_complete_samples(this, buffer, stereo_output_); } -void MultiSpeaker::speaker_did_change_input_clock(Speaker *speaker) { +void MultiSpeaker::speaker_did_change_input_clock(Speaker *const speaker) { auto delegate = delegate_.load(std::memory_order_relaxed); if(!delegate) return; { @@ -80,7 +80,7 @@ void MultiSpeaker::speaker_did_change_input_clock(Speaker *speaker) { delegate->speaker_did_change_input_clock(this); } -void MultiSpeaker::set_new_front_machine(::Machine::DynamicMachine *machine) { +void MultiSpeaker::set_new_front_machine(::Machine::DynamicMachine *const machine) { { std::lock_guard lock_guard(front_speaker_mutex_); front_speaker_ = machine->audio_producer()->get_speaker(); diff --git a/Analyser/Dynamic/MultiMachine/Implementation/MultiSpeaker.hpp b/Analyser/Dynamic/MultiMachine/Implementation/MultiSpeaker.hpp index 0a1990157..2e44e4de5 100644 --- a/Analyser/Dynamic/MultiMachine/Implementation/MultiSpeaker.hpp +++ b/Analyser/Dynamic/MultiMachine/Implementation/MultiSpeaker.hpp @@ -25,32 +25,32 @@ namespace Analyser::Dynamic { abreast of the current frontmost machine. */ class MultiSpeaker: public Outputs::Speaker::Speaker, Outputs::Speaker::Speaker::Delegate { - public: - /*! - Provides a construction mechanism that may return nullptr, in the case that all included - machines return nullptr as their speaker. - */ - static MultiSpeaker *create(const std::vector> &machines); +public: + /*! + Provides a construction mechanism that may return nullptr, in the case that all included + machines return nullptr as their speaker. + */ + static MultiSpeaker *create(const std::vector> &); - /// This class requires the caller to nominate changes in the frontmost machine. - void set_new_front_machine(::Machine::DynamicMachine *machine); + /// This class requires the caller to nominate changes in the frontmost machine. + void set_new_front_machine(::Machine::DynamicMachine *); - // Below is the standard Outputs::Speaker::Speaker interface; see there for documentation. - float get_ideal_clock_rate_in_range(float minimum, float maximum) override; - void set_computed_output_rate(float cycles_per_second, int buffer_size, bool stereo) override; - bool get_is_stereo() override; - void set_output_volume(float) override; + // Below is the standard Outputs::Speaker::Speaker interface; see there for documentation. + float get_ideal_clock_rate_in_range(float minimum, float maximum) override; + void set_computed_output_rate(float cycles_per_second, int buffer_size, bool stereo) override; + bool get_is_stereo() override; + void set_output_volume(float) override; - private: - void speaker_did_complete_samples(Speaker *speaker, const std::vector &buffer) final; - void speaker_did_change_input_clock(Speaker *speaker) final; - MultiSpeaker(const std::vector &speakers); +private: + void speaker_did_complete_samples(Speaker *speaker, const std::vector &buffer) final; + void speaker_did_change_input_clock(Speaker *speaker) final; + MultiSpeaker(const std::vector &speakers); - std::vector speakers_; - Outputs::Speaker::Speaker *front_speaker_ = nullptr; - std::mutex front_speaker_mutex_; + std::vector speakers_; + Outputs::Speaker::Speaker *front_speaker_ = nullptr; + std::mutex front_speaker_mutex_; - bool stereo_output_ = false; + bool stereo_output_ = false; }; } diff --git a/Analyser/Dynamic/MultiMachine/MultiMachine.cpp b/Analyser/Dynamic/MultiMachine/MultiMachine.cpp index 2f20b4a30..e45ee8d38 100644 --- a/Analyser/Dynamic/MultiMachine/MultiMachine.cpp +++ b/Analyser/Dynamic/MultiMachine/MultiMachine.cpp @@ -27,7 +27,8 @@ MultiMachine::MultiMachine(std::vector> &&machin audio_producer_(machines_, machines_mutex_), joystick_machine_(machines_), keyboard_machine_(machines_), - media_target_(machines_) { + media_target_(machines_) +{ timed_machine_.set_delegate(this); } @@ -35,13 +36,13 @@ Activity::Source *MultiMachine::activity_source() { return nullptr; // TODO } -#define Provider(type, name, member) \ - type *MultiMachine::name() { \ - if(has_picked_) { \ +#define Provider(type, name, member) \ + type *MultiMachine::name() { \ + if(has_picked_) { \ return machines_.front()->name(); \ - } else { \ - return &member; \ - } \ + } else { \ + return &member; \ + } \ } Provider(Configurable::Device, configurable_device, configurable_) diff --git a/Analyser/Dynamic/MultiMachine/MultiMachine.hpp b/Analyser/Dynamic/MultiMachine/MultiMachine.hpp index 2b42b4f97..0dcdd965a 100644 --- a/Analyser/Dynamic/MultiMachine/MultiMachine.hpp +++ b/Analyser/Dynamic/MultiMachine/MultiMachine.hpp @@ -38,44 +38,44 @@ namespace Analyser::Dynamic { the others in the set, that machine stops running. */ class MultiMachine: public ::Machine::DynamicMachine, public MultiTimedMachine::Delegate { - public: - /*! - Allows a potential MultiMachine creator to enquire as to whether there's any benefit in - requesting this class as a proxy. +public: + /*! + Allows a potential MultiMachine creator to enquire as to whether there's any benefit in + requesting this class as a proxy. - @returns @c true if the multimachine would discard all but the first machine in this list; - @c false otherwise. - */ - static bool would_collapse(const std::vector> &machines); - MultiMachine(std::vector> &&machines); + @returns @c true if the multimachine would discard all but the first machine in this list; + @c false otherwise. + */ + static bool would_collapse(const std::vector> &); + MultiMachine(std::vector> &&); - Activity::Source *activity_source() final; - Configurable::Device *configurable_device() final; - MachineTypes::TimedMachine *timed_machine() final; - MachineTypes::ScanProducer *scan_producer() final; - MachineTypes::AudioProducer *audio_producer() final; - MachineTypes::JoystickMachine *joystick_machine() final; - MachineTypes::KeyboardMachine *keyboard_machine() final; - MachineTypes::MouseMachine *mouse_machine() final; - MachineTypes::MediaTarget *media_target() final; - void *raw_pointer() final; + Activity::Source *activity_source() final; + Configurable::Device *configurable_device() final; + MachineTypes::TimedMachine *timed_machine() final; + MachineTypes::ScanProducer *scan_producer() final; + MachineTypes::AudioProducer *audio_producer() final; + MachineTypes::JoystickMachine *joystick_machine() final; + MachineTypes::KeyboardMachine *keyboard_machine() final; + MachineTypes::MouseMachine *mouse_machine() final; + MachineTypes::MediaTarget *media_target() final; + void *raw_pointer() final; - private: - void did_run_machines(MultiTimedMachine *) final; +private: + void did_run_machines(MultiTimedMachine *) final; - std::vector> machines_; - std::recursive_mutex machines_mutex_; + std::vector> machines_; + std::recursive_mutex machines_mutex_; - MultiConfigurable configurable_; - MultiTimedMachine timed_machine_; - MultiScanProducer scan_producer_; - MultiAudioProducer audio_producer_; - MultiJoystickMachine joystick_machine_; - MultiKeyboardMachine keyboard_machine_; - MultiMediaTarget media_target_; + MultiConfigurable configurable_; + MultiTimedMachine timed_machine_; + MultiScanProducer scan_producer_; + MultiAudioProducer audio_producer_; + MultiJoystickMachine joystick_machine_; + MultiKeyboardMachine keyboard_machine_; + MultiMediaTarget media_target_; - void pick_first(); - bool has_picked_ = false; + void pick_first(); + bool has_picked_ = false; }; } diff --git a/Analyser/Static/Acorn/Disk.cpp b/Analyser/Static/Acorn/Disk.cpp index 2b38aaca4..9447005b3 100644 --- a/Analyser/Static/Acorn/Disk.cpp +++ b/Analyser/Static/Acorn/Disk.cpp @@ -49,27 +49,39 @@ std::unique_ptr Analyser::Static::Acorn::GetDFSCatalogue(const std::s char name[10]; snprintf(name, 10, "%c.%.7s", names->samples[0][file_offset + 7] & 0x7f, &names->samples[0][file_offset]); new_file.name = name; - new_file.load_address = uint32_t(details->samples[0][file_offset] | (details->samples[0][file_offset+1] << 8) | ((details->samples[0][file_offset+6]&0x0c) << 14)); - new_file.execution_address = uint32_t(details->samples[0][file_offset+2] | (details->samples[0][file_offset+3] << 8) | ((details->samples[0][file_offset+6]&0xc0) << 10)); + new_file.load_address = uint32_t( + details->samples[0][file_offset] | + (details->samples[0][file_offset+1] << 8) | + ((details->samples[0][file_offset+6]&0x0c) << 14) + ); + new_file.execution_address = uint32_t( + details->samples[0][file_offset+2] | + (details->samples[0][file_offset+3] << 8) | + ((details->samples[0][file_offset+6]&0xc0) << 10) + ); if(names->samples[0][file_offset + 7] & 0x80) { // File is locked; it may not be altered or deleted. new_file.flags |= File::Flags::Locked; } - long data_length = long(details->samples[0][file_offset+4] | (details->samples[0][file_offset+5] << 8) | ((details->samples[0][file_offset+6]&0x30) << 12)); + auto data_length = long( + details->samples[0][file_offset+4] | + (details->samples[0][file_offset+5] << 8) | + ((details->samples[0][file_offset+6]&0x30) << 12) + ); int start_sector = details->samples[0][file_offset+7] | ((details->samples[0][file_offset+6]&0x03) << 8); new_file.data.reserve(size_t(data_length)); if(start_sector < 2) continue; while(data_length > 0) { - uint8_t sector = uint8_t(start_sector % 10); - uint8_t track = uint8_t(start_sector / 10); - start_sector++; + const uint8_t sector = uint8_t(start_sector % 10); + const uint8_t track = uint8_t(start_sector / 10); + ++start_sector; const Storage::Encodings::MFM::Sector *next_sector = parser.sector(0, track, sector); if(!next_sector) break; - long length_from_sector = std::min(data_length, 256l); + const long length_from_sector = std::min(data_length, 256l); new_file.data.insert(new_file.data.end(), next_sector->samples[0].begin(), next_sector->samples[0].begin() + length_from_sector); data_length -= length_from_sector; } @@ -133,7 +145,8 @@ std::unique_ptr Analyser::Static::Acorn::GetADFSCatalogue(const std:: } // Parse the root directory, at least. - for(std::size_t file_offset = 0x005; file_offset < (catalogue->has_large_sectors ? 0x7d7 : 0x4cb); file_offset += 0x1a) { + const std::size_t directory_extent = catalogue->has_large_sectors ? 0x7d7 : 0x4cb; + for(std::size_t file_offset = 0x005; file_offset < directory_extent; file_offset += 0x1a) { // Obtain the name, which will be at most ten characters long, and will // be terminated by either a NULL character or a \r. char name[11]{}; @@ -190,11 +203,16 @@ std::unique_ptr Analyser::Static::Acorn::GetADFSCatalogue(const std:: new_file.data.reserve(size); while(new_file.data.size() < size) { - const Storage::Encodings::MFM::Sector *const sector = parser.sector(start_sector / (80 * 16), (start_sector / 16) % 80, start_sector % 16); + const Storage::Encodings::MFM::Sector *const sector = + parser.sector(start_sector / (80 * 16), (start_sector / 16) % 80, start_sector % 16); if(!sector) break; const auto length_from_sector = std::min(size - new_file.data.size(), sector->samples[0].size()); - new_file.data.insert(new_file.data.end(), sector->samples[0].begin(), sector->samples[0].begin() + ssize_t(length_from_sector)); + new_file.data.insert( + new_file.data.end(), + sector->samples[0].begin(), + sector->samples[0].begin() + ssize_t(length_from_sector) + ); ++start_sector; } diff --git a/Analyser/Static/Acorn/Disk.hpp b/Analyser/Static/Acorn/Disk.hpp index 6eab662cb..4ec65a486 100644 --- a/Analyser/Static/Acorn/Disk.hpp +++ b/Analyser/Static/Acorn/Disk.hpp @@ -27,7 +27,7 @@ struct Catalogue { } bootOption; }; -std::unique_ptr GetDFSCatalogue(const std::shared_ptr &disk); -std::unique_ptr GetADFSCatalogue(const std::shared_ptr &disk); +std::unique_ptr GetDFSCatalogue(const std::shared_ptr &); +std::unique_ptr GetADFSCatalogue(const std::shared_ptr &); } diff --git a/Analyser/Static/Acorn/StaticAnalyser.cpp b/Analyser/Static/Acorn/StaticAnalyser.cpp index 6c4a830d6..7f3dc7468 100644 --- a/Analyser/Static/Acorn/StaticAnalyser.cpp +++ b/Analyser/Static/Acorn/StaticAnalyser.cpp @@ -20,7 +20,7 @@ using namespace Analyser::Static::Acorn; static std::vector> - AcornCartridgesFrom(const std::vector> &cartridges) { +AcornCartridgesFrom(const std::vector> &cartridges) { std::vector> acorn_cartridges; for(const auto &cartridge : cartridges) { @@ -62,7 +62,11 @@ static std::vector> return acorn_cartridges; } -Analyser::Static::TargetList Analyser::Static::Acorn::GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType) { +Analyser::Static::TargetList Analyser::Static::Acorn::GetTargets( + const Media &media, + const std::string &file_name, + TargetPlatform::IntType +) { auto target8bit = std::make_unique(); auto targetArchimedes = std::make_unique(); @@ -121,7 +125,7 @@ Analyser::Static::TargetList Analyser::Static::Acorn::GetTargets(const Media &me target8bit->has_pres_adfs = bool(adfs_catalogue); // Check whether a simple shift+break will do for loading this disk. - Catalogue::BootOption bootOption = (dfs_catalogue ?: adfs_catalogue)->bootOption; + const auto bootOption = (dfs_catalogue ?: adfs_catalogue)->bootOption; if(bootOption != Catalogue::BootOption::None) { target8bit->should_shift_restart = true; } else { diff --git a/Analyser/Static/Acorn/StaticAnalyser.hpp b/Analyser/Static/Acorn/StaticAnalyser.hpp index 35331b65f..6a845e89e 100644 --- a/Analyser/Static/Acorn/StaticAnalyser.hpp +++ b/Analyser/Static/Acorn/StaticAnalyser.hpp @@ -14,6 +14,6 @@ namespace Analyser::Static::Acorn { -TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms); +TargetList GetTargets(const Media &, const std::string &file_name, TargetPlatform::IntType potential_platforms); } diff --git a/Analyser/Static/Acorn/Tape.cpp b/Analyser/Static/Acorn/Tape.cpp index 8badda594..d0eeb11d3 100644 --- a/Analyser/Static/Acorn/Tape.cpp +++ b/Analyser/Static/Acorn/Tape.cpp @@ -15,7 +15,10 @@ using namespace Analyser::Static::Acorn; -static std::unique_ptr GetNextChunk(const std::shared_ptr &tape, Storage::Tape::Acorn::Parser &parser) { +static std::unique_ptr GetNextChunk( + const std::shared_ptr &tape, + Storage::Tape::Acorn::Parser &parser +) { auto new_chunk = std::make_unique(); int shift_register = 0; @@ -56,7 +59,7 @@ static std::unique_ptr GetNextChunk(const std::shared_ptrblock_flag = uint8_t(parser.get_next_byte(tape)); new_chunk->next_address = uint32_t(parser.get_next_word(tape)); - uint16_t calculated_header_crc = parser.get_crc(); + const uint16_t calculated_header_crc = parser.get_crc(); uint16_t stored_header_crc = uint16_t(parser.get_next_short(tape)); stored_header_crc = uint16_t((stored_header_crc >> 8) | (stored_header_crc << 8)); new_chunk->header_crc_matched = stored_header_crc == calculated_header_crc; diff --git a/Analyser/Static/Acorn/Tape.hpp b/Analyser/Static/Acorn/Tape.hpp index d6978ba61..cfe2902cb 100644 --- a/Analyser/Static/Acorn/Tape.hpp +++ b/Analyser/Static/Acorn/Tape.hpp @@ -15,6 +15,6 @@ namespace Analyser::Static::Acorn { -std::vector GetFiles(const std::shared_ptr &tape); +std::vector GetFiles(const std::shared_ptr &); } diff --git a/Analyser/Static/Amiga/StaticAnalyser.cpp b/Analyser/Static/Amiga/StaticAnalyser.cpp index 5bdaf7bae..138bcd44d 100644 --- a/Analyser/Static/Amiga/StaticAnalyser.cpp +++ b/Analyser/Static/Amiga/StaticAnalyser.cpp @@ -9,7 +9,11 @@ #include "StaticAnalyser.hpp" #include "Target.hpp" -Analyser::Static::TargetList Analyser::Static::Amiga::GetTargets(const Media &media, const std::string &, TargetPlatform::IntType) { +Analyser::Static::TargetList Analyser::Static::Amiga::GetTargets( + const Media &media, + const std::string &, + TargetPlatform::IntType +) { // This analyser can comprehend disks and mass-storage devices only. if(media.disks.empty()) return {}; diff --git a/Analyser/Static/Amiga/StaticAnalyser.hpp b/Analyser/Static/Amiga/StaticAnalyser.hpp index 4e6fa7501..19a96e6a0 100644 --- a/Analyser/Static/Amiga/StaticAnalyser.hpp +++ b/Analyser/Static/Amiga/StaticAnalyser.hpp @@ -14,6 +14,6 @@ namespace Analyser::Static::Amiga { -TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms); +TargetList GetTargets(const Media &, const std::string &file_name, TargetPlatform::IntType potential_platforms); } diff --git a/Analyser/Static/AmstradCPC/StaticAnalyser.cpp b/Analyser/Static/AmstradCPC/StaticAnalyser.cpp index 65854ab26..a9e505505 100644 --- a/Analyser/Static/AmstradCPC/StaticAnalyser.cpp +++ b/Analyser/Static/AmstradCPC/StaticAnalyser.cpp @@ -63,8 +63,8 @@ std::string RunCommandFor(const Storage::Disk::CPM::File &file) { void InspectCatalogue( const Storage::Disk::CPM::Catalogue &catalogue, - const std::unique_ptr &target) { - + const std::unique_ptr &target +) { std::vector candidate_files; candidate_files.reserve(catalogue.files.size()); for(const auto &file : catalogue.files) { @@ -158,7 +158,10 @@ void InspectCatalogue( target->loading_command = "cat\n"; } -bool CheckBootSector(const std::shared_ptr &disk, const std::unique_ptr &target) { +bool CheckBootSector( + const std::shared_ptr &disk, + const std::unique_ptr &target +) { Storage::Encodings::MFM::Parser parser(Storage::Encodings::MFM::Density::Double, disk); const Storage::Encodings::MFM::Sector *boot_sector = parser.sector(0, 0, 0x41); if(boot_sector != nullptr && !boot_sector->samples.empty() && boot_sector->samples[0].size() == 512) { @@ -204,7 +207,11 @@ bool IsAmstradTape(const std::shared_ptr &tape) { } // namespace -Analyser::Static::TargetList Analyser::Static::AmstradCPC::GetTargets(const Media &media, const std::string &, TargetPlatform::IntType) { +Analyser::Static::TargetList Analyser::Static::AmstradCPC::GetTargets( + const Media &media, + const std::string &, + TargetPlatform::IntType +) { TargetList destination; auto target = std::make_unique(); target->confidence = 0.5; @@ -233,7 +240,8 @@ Analyser::Static::TargetList Analyser::Static::AmstradCPC::GetTargets(const Medi for(auto &disk: media.disks) { // Check for an ordinary catalogue, making sure this isn't actually a ZX Spectrum disk. - std::unique_ptr data_catalogue = Storage::Disk::CPM::GetCatalogue(disk, data_format, false); + std::unique_ptr data_catalogue = + Storage::Disk::CPM::GetCatalogue(disk, data_format, false); if(data_catalogue && !data_catalogue->is_zx_spectrum_booter()) { InspectCatalogue(*data_catalogue, target); target->media.disks.push_back(disk); @@ -247,7 +255,8 @@ Analyser::Static::TargetList Analyser::Static::AmstradCPC::GetTargets(const Medi } // Failing that check for a system catalogue. - std::unique_ptr system_catalogue = Storage::Disk::CPM::GetCatalogue(disk, system_format, false); + std::unique_ptr system_catalogue = + Storage::Disk::CPM::GetCatalogue(disk, system_format, false); if(system_catalogue && !system_catalogue->is_zx_spectrum_booter()) { InspectCatalogue(*system_catalogue, target); target->media.disks.push_back(disk); diff --git a/Analyser/Static/AmstradCPC/StaticAnalyser.hpp b/Analyser/Static/AmstradCPC/StaticAnalyser.hpp index 234a069c5..4c8ca0eaa 100644 --- a/Analyser/Static/AmstradCPC/StaticAnalyser.hpp +++ b/Analyser/Static/AmstradCPC/StaticAnalyser.hpp @@ -14,6 +14,6 @@ namespace Analyser::Static::AmstradCPC { -TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms); +TargetList GetTargets(const Media &, const std::string &file_name, TargetPlatform::IntType potential_platforms); } diff --git a/Analyser/Static/AppleII/StaticAnalyser.cpp b/Analyser/Static/AppleII/StaticAnalyser.cpp index 3f379cf47..e22a53915 100644 --- a/Analyser/Static/AppleII/StaticAnalyser.cpp +++ b/Analyser/Static/AppleII/StaticAnalyser.cpp @@ -9,7 +9,11 @@ #include "StaticAnalyser.hpp" #include "Target.hpp" -Analyser::Static::TargetList Analyser::Static::AppleII::GetTargets(const Media &media, const std::string &, TargetPlatform::IntType) { +Analyser::Static::TargetList Analyser::Static::AppleII::GetTargets( + const Media &media, + const std::string &, + TargetPlatform::IntType +) { auto target = std::make_unique(); target->media = media; diff --git a/Analyser/Static/AppleII/StaticAnalyser.hpp b/Analyser/Static/AppleII/StaticAnalyser.hpp index fa31746ef..e4c4795a8 100644 --- a/Analyser/Static/AppleII/StaticAnalyser.hpp +++ b/Analyser/Static/AppleII/StaticAnalyser.hpp @@ -14,6 +14,6 @@ namespace Analyser::Static::AppleII { -TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms); +TargetList GetTargets(const Media &, const std::string &file_name, TargetPlatform::IntType potential_platforms); } diff --git a/Analyser/Static/AppleIIgs/StaticAnalyser.cpp b/Analyser/Static/AppleIIgs/StaticAnalyser.cpp index 56c3f9ab3..7b812a6e6 100644 --- a/Analyser/Static/AppleIIgs/StaticAnalyser.cpp +++ b/Analyser/Static/AppleIIgs/StaticAnalyser.cpp @@ -9,7 +9,11 @@ #include "StaticAnalyser.hpp" #include "Target.hpp" -Analyser::Static::TargetList Analyser::Static::AppleIIgs::GetTargets(const Media &media, const std::string &, TargetPlatform::IntType) { +Analyser::Static::TargetList Analyser::Static::AppleIIgs::GetTargets( + const Media &media, + const std::string &, + TargetPlatform::IntType +) { auto target = std::make_unique(); target->media = media; diff --git a/Analyser/Static/AppleIIgs/StaticAnalyser.hpp b/Analyser/Static/AppleIIgs/StaticAnalyser.hpp index ee500b91a..8185f366a 100644 --- a/Analyser/Static/AppleIIgs/StaticAnalyser.hpp +++ b/Analyser/Static/AppleIIgs/StaticAnalyser.hpp @@ -14,6 +14,6 @@ namespace Analyser::Static::AppleIIgs { -TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms); +TargetList GetTargets(const Media &, const std::string &file_name, TargetPlatform::IntType potential_platforms); } diff --git a/Analyser/Static/Atari2600/StaticAnalyser.cpp b/Analyser/Static/Atari2600/StaticAnalyser.cpp index a84dc6ad5..6db482811 100644 --- a/Analyser/Static/Atari2600/StaticAnalyser.cpp +++ b/Analyser/Static/Atari2600/StaticAnalyser.cpp @@ -33,11 +33,13 @@ static void DeterminePagingFor2kCartridge(Target &target, const Storage::Cartrid // Assume that any kind of store that looks likely to be intended for large amounts of memory implies // large amounts of memory. bool has_wide_area_store = false; - for(std::map::value_type &entry : high_location_disassembly.instructions_by_address) { + for(const auto &entry : high_location_disassembly.instructions_by_address) { + using Instruction = Analyser::Static::MOS6502::Instruction; if(entry.second.operation == Analyser::Static::MOS6502::Instruction::STA) { - has_wide_area_store |= entry.second.addressing_mode == Analyser::Static::MOS6502::Instruction::Indirect; - has_wide_area_store |= entry.second.addressing_mode == Analyser::Static::MOS6502::Instruction::IndexedIndirectX; - has_wide_area_store |= entry.second.addressing_mode == Analyser::Static::MOS6502::Instruction::IndirectIndexedY; + has_wide_area_store |= + entry.second.addressing_mode == Instruction::Indirect || + entry.second.addressing_mode == Instruction::IndexedIndirectX || + entry.second.addressing_mode == Instruction::IndirectIndexedY; if(has_wide_area_store) break; } @@ -50,13 +52,21 @@ static void DeterminePagingFor2kCartridge(Target &target, const Storage::Cartrid if(has_wide_area_store) target.paging_model = Target::PagingModel::CommaVid; } -static void DeterminePagingFor8kCartridge(Target &target, const Storage::Cartridge::Cartridge::Segment &segment, const Analyser::Static::MOS6502::Disassembly &disassembly) { +static void DeterminePagingFor8kCartridge( + Target &target, + const Storage::Cartridge::Cartridge::Segment &segment, + const Analyser::Static::MOS6502::Disassembly &disassembly +) { // Activision stack titles have their vectors at the top of the low 4k, not the top, and // always list 0xf000 as both vectors; they do not repeat them, and, inexplicably, they all // issue an SEI as their first instruction (maybe some sort of relic of the development environment?). if( - segment.data[4095] == 0xf0 && segment.data[4093] == 0xf0 && segment.data[4094] == 0x00 && segment.data[4092] == 0x00 && - (segment.data[8191] != 0xf0 || segment.data[8189] != 0xf0 || segment.data[8190] != 0x00 || segment.data[8188] != 0x00) && + segment.data[4095] == 0xf0 && segment.data[4093] == 0xf0 && + segment.data[4094] == 0x00 && segment.data[4092] == 0x00 && + ( + segment.data[8191] != 0xf0 || segment.data[8189] != 0xf0 || + segment.data[8190] != 0x00 || segment.data[8188] != 0x00 + ) && segment.data[0] == 0x78 ) { target.paging_model = Target::PagingModel::ActivisionStack; @@ -88,7 +98,11 @@ static void DeterminePagingFor8kCartridge(Target &target, const Storage::Cartrid else if(tigervision_access_count > atari_access_count) target.paging_model = Target::PagingModel::Tigervision; } -static void DeterminePagingFor16kCartridge(Target &target, const Storage::Cartridge::Cartridge::Segment &, const Analyser::Static::MOS6502::Disassembly &disassembly) { +static void DeterminePagingFor16kCartridge( + Target &target, + const Storage::Cartridge::Cartridge::Segment &, + const Analyser::Static::MOS6502::Disassembly &disassembly +) { // Make an assumption that this is the Atari paging model. target.paging_model = Target::PagingModel::Atari16k; @@ -108,7 +122,11 @@ static void DeterminePagingFor16kCartridge(Target &target, const Storage::Cartri if(mnetwork_access_count > atari_access_count) target.paging_model = Target::PagingModel::MNetwork; } -static void DeterminePagingFor64kCartridge(Target &target, const Storage::Cartridge::Cartridge::Segment &, const Analyser::Static::MOS6502::Disassembly &disassembly) { +static void DeterminePagingFor64kCartridge( + Target &target, + const Storage::Cartridge::Cartridge::Segment &, + const Analyser::Static::MOS6502::Disassembly &disassembly +) { // Make an assumption that this is a Tigervision if there is a write to 3F. target.paging_model = (disassembly.external_stores.find(0x3f) != disassembly.external_stores.end()) ? @@ -121,8 +139,12 @@ static void DeterminePagingForCartridge(Target &target, const Storage::Cartridge return; } - const uint16_t entry_address = uint16_t(segment.data[segment.data.size() - 4] | (segment.data[segment.data.size() - 3] << 8)); - const uint16_t break_address = uint16_t(segment.data[segment.data.size() - 2] | (segment.data[segment.data.size() - 1] << 8)); + const auto word = [](const uint8_t low, const uint8_t high) { + return uint16_t(low | (high << 8)); + }; + + const auto entry_address = word(segment.data[segment.data.size() - 4], segment.data[segment.data.size() - 3]); + const auto break_address = word(segment.data[segment.data.size() - 2], segment.data[segment.data.size() - 1]); std::function address_mapper = [](uint16_t address) { if(!(address & 0x1000)) return size_t(-1); @@ -130,27 +152,16 @@ static void DeterminePagingForCartridge(Target &target, const Storage::Cartridge }; const std::vector final_4k(segment.data.end() - 4096, segment.data.end()); - Analyser::Static::MOS6502::Disassembly disassembly = Analyser::Static::MOS6502::Disassemble(final_4k, address_mapper, {entry_address, break_address}); + const auto disassembly = + Analyser::Static::MOS6502::Disassemble(final_4k, address_mapper, {entry_address, break_address}); switch(segment.data.size()) { - case 8192: - DeterminePagingFor8kCartridge(target, segment, disassembly); - break; - case 10495: - target.paging_model = Target::PagingModel::Pitfall2; - break; - case 12288: - target.paging_model = Target::PagingModel::CBSRamPlus; - break; - case 16384: - DeterminePagingFor16kCartridge(target, segment, disassembly); - break; - case 32768: - target.paging_model = Target::PagingModel::Atari32k; - break; - case 65536: - DeterminePagingFor64kCartridge(target, segment, disassembly); - break; + case 8192: DeterminePagingFor8kCartridge(target, segment, disassembly); break; + case 10495: target.paging_model = Target::PagingModel::Pitfall2; break; + case 12288: target.paging_model = Target::PagingModel::CBSRamPlus; break; + case 16384: DeterminePagingFor16kCartridge(target, segment, disassembly); break; + case 32768: target.paging_model = Target::PagingModel::Atari32k; break; + case 65536: DeterminePagingFor64kCartridge(target, segment, disassembly); break; default: break; } @@ -177,7 +188,11 @@ static void DeterminePagingForCartridge(Target &target, const Storage::Cartridge } } -Analyser::Static::TargetList Analyser::Static::Atari2600::GetTargets(const Media &media, const std::string &, TargetPlatform::IntType) { +Analyser::Static::TargetList Analyser::Static::Atari2600::GetTargets( + const Media &media, + const std::string &, + TargetPlatform::IntType +) { // TODO: sanity checking; is this image really for an Atari 2600? auto target = std::make_unique(); target->confidence = 0.5; diff --git a/Analyser/Static/Atari2600/StaticAnalyser.hpp b/Analyser/Static/Atari2600/StaticAnalyser.hpp index 1c7e747ce..a23b8a8bf 100644 --- a/Analyser/Static/Atari2600/StaticAnalyser.hpp +++ b/Analyser/Static/Atari2600/StaticAnalyser.hpp @@ -14,6 +14,6 @@ namespace Analyser::Static::Atari2600 { -TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms); +TargetList GetTargets(const Media &, const std::string &file_name, TargetPlatform::IntType potential_platforms); } diff --git a/Analyser/Static/AtariST/StaticAnalyser.cpp b/Analyser/Static/AtariST/StaticAnalyser.cpp index f0524b353..4621755f8 100644 --- a/Analyser/Static/AtariST/StaticAnalyser.cpp +++ b/Analyser/Static/AtariST/StaticAnalyser.cpp @@ -9,7 +9,11 @@ #include "StaticAnalyser.hpp" #include "Target.hpp" -Analyser::Static::TargetList Analyser::Static::AtariST::GetTargets(const Media &media, const std::string &, TargetPlatform::IntType) { +Analyser::Static::TargetList Analyser::Static::AtariST::GetTargets( + const Media &media, + const std::string &, + TargetPlatform::IntType +) { // This analyser can comprehend disks and mass-storage devices only. if(media.disks.empty()) return {}; diff --git a/Analyser/Static/AtariST/StaticAnalyser.hpp b/Analyser/Static/AtariST/StaticAnalyser.hpp index a51bd3370..555bd76ec 100644 --- a/Analyser/Static/AtariST/StaticAnalyser.hpp +++ b/Analyser/Static/AtariST/StaticAnalyser.hpp @@ -14,6 +14,6 @@ namespace Analyser::Static::AtariST { -TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms); +TargetList GetTargets(const Media &, const std::string &file_name, TargetPlatform::IntType potential_platforms); } diff --git a/Analyser/Static/Coleco/StaticAnalyser.cpp b/Analyser/Static/Coleco/StaticAnalyser.cpp index 08fd64744..5270825a9 100644 --- a/Analyser/Static/Coleco/StaticAnalyser.cpp +++ b/Analyser/Static/Coleco/StaticAnalyser.cpp @@ -9,7 +9,7 @@ #include "StaticAnalyser.hpp" static std::vector> - ColecoCartridgesFrom(const std::vector> &cartridges) { +ColecoCartridgesFrom(const std::vector> &cartridges) { std::vector> coleco_cartridges; for(const auto &cartridge : cartridges) { @@ -52,7 +52,11 @@ static std::vector> return coleco_cartridges; } -Analyser::Static::TargetList Analyser::Static::Coleco::GetTargets(const Media &media, const std::string &, TargetPlatform::IntType) { +Analyser::Static::TargetList Analyser::Static::Coleco::GetTargets( + const Media &media, + const std::string &, + TargetPlatform::IntType +) { TargetList targets; auto target = std::make_unique(Machine::ColecoVision); target->confidence = 1.0f - 1.0f / 32768.0f; diff --git a/Analyser/Static/Coleco/StaticAnalyser.hpp b/Analyser/Static/Coleco/StaticAnalyser.hpp index b2f6ef265..9018fc445 100644 --- a/Analyser/Static/Coleco/StaticAnalyser.hpp +++ b/Analyser/Static/Coleco/StaticAnalyser.hpp @@ -14,6 +14,6 @@ namespace Analyser::Static::Coleco { -TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms); +TargetList GetTargets(const Media &, const std::string &file_name, TargetPlatform::IntType potential_platforms); } diff --git a/Analyser/Static/Commodore/Disk.cpp b/Analyser/Static/Commodore/Disk.cpp index e759a636f..2a72dcb54 100644 --- a/Analyser/Static/Commodore/Disk.cpp +++ b/Analyser/Static/Commodore/Disk.cpp @@ -18,155 +18,155 @@ using namespace Analyser::Static::Commodore; class CommodoreGCRParser: public Storage::Disk::Controller { - public: - CommodoreGCRParser() : Storage::Disk::Controller(4000000), shift_register_(0), track_(1) { - emplace_drive(4000000, 300, 2); - set_drive(1); - get_drive().set_motor_on(true); - } +public: + CommodoreGCRParser() : Storage::Disk::Controller(4000000), shift_register_(0), track_(1) { + emplace_drive(4000000, 300, 2); + set_drive(1); + get_drive().set_motor_on(true); + } - struct Sector { - uint8_t sector, track; - std::array data; - bool header_checksum_matched; - bool data_checksum_matched; - }; + struct Sector { + uint8_t sector, track; + std::array data; + bool header_checksum_matched; + bool data_checksum_matched; + }; - /*! - Attempts to read the sector located at @c track and @c sector. + /*! + Attempts to read the sector located at @c track and @c sector. - @returns a sector if one was found; @c nullptr otherwise. - */ - std::shared_ptr sector(uint8_t track, uint8_t sector) { - int difference = int(track) - int(track_); - track_ = track; + @returns a sector if one was found; @c nullptr otherwise. + */ + std::shared_ptr sector(const uint8_t track, const uint8_t sector) { + int difference = int(track) - int(track_); + track_ = track; - if(difference) { - int direction = difference < 0 ? -1 : 1; - difference *= direction; + if(difference) { + const int direction = difference < 0 ? -1 : 1; + difference *= direction; - for(int c = 0; c < difference; c++) { - get_drive().step(Storage::Disk::HeadPosition(direction)); - } - - unsigned int zone = 3; - if(track >= 18) zone = 2; - else if(track >= 25) zone = 1; - else if(track >= 31) zone = 0; - set_expected_bit_length(Storage::Encodings::CommodoreGCR::length_of_a_bit_in_time_zone(zone)); + for(int c = 0; c < difference; c++) { + get_drive().step(Storage::Disk::HeadPosition(direction)); } - return get_sector(sector); + unsigned int zone = 3; + if(track >= 18) zone = 2; + else if(track >= 25) zone = 1; + else if(track >= 31) zone = 0; + set_expected_bit_length(Storage::Encodings::CommodoreGCR::length_of_a_bit_in_time_zone(zone)); } - void set_disk(const std::shared_ptr &disk) { - get_drive().set_disk(disk); + return get_sector(sector); + } + + void set_disk(const std::shared_ptr &disk) { + get_drive().set_disk(disk); + } + +private: + unsigned int shift_register_; + int index_count_; + int bit_count_; + uint8_t track_; + std::shared_ptr sector_cache_[65536]; + + void process_input_bit(const int value) { + shift_register_ = ((shift_register_ << 1) | unsigned(value)) & 0x3ff; + bit_count_++; + } + + unsigned int proceed_to_next_block(const int max_index_count) { + // find GCR lead-in + proceed_to_shift_value(0x3ff); + if(shift_register_ != 0x3ff) return 0xff; + + // find end of lead-in + while(shift_register_ == 0x3ff && index_count_ < max_index_count) { + run_for(Cycles(1)); } - private: - unsigned int shift_register_; - int index_count_; - int bit_count_; - uint8_t track_; - std::shared_ptr sector_cache_[65536]; - - void process_input_bit(int value) { - shift_register_ = ((shift_register_ << 1) | unsigned(value)) & 0x3ff; - bit_count_++; + // continue for a further nine bits + bit_count_ = 0; + while(bit_count_ < 9 && index_count_ < max_index_count) { + run_for(Cycles(1)); } - unsigned int proceed_to_next_block(int max_index_count) { - // find GCR lead-in - proceed_to_shift_value(0x3ff); - if(shift_register_ != 0x3ff) return 0xff; + return Storage::Encodings::CommodoreGCR::decoding_from_dectet(shift_register_); + } - // find end of lead-in - while(shift_register_ == 0x3ff && index_count_ < max_index_count) { - run_for(Cycles(1)); - } + unsigned int get_next_byte() { + bit_count_ = 0; + while(bit_count_ < 10) run_for(Cycles(1)); + return Storage::Encodings::CommodoreGCR::decoding_from_dectet(shift_register_); + } - // continue for a further nine bits - bit_count_ = 0; - while(bit_count_ < 9 && index_count_ < max_index_count) { - run_for(Cycles(1)); - } - - return Storage::Encodings::CommodoreGCR::decoding_from_dectet(shift_register_); + void proceed_to_shift_value(const unsigned int shift_value) { + const int max_index_count = index_count_ + 2; + while(shift_register_ != shift_value && index_count_ < max_index_count) { + run_for(Cycles(1)); } + } - unsigned int get_next_byte() { - bit_count_ = 0; - while(bit_count_ < 10) run_for(Cycles(1)); - return Storage::Encodings::CommodoreGCR::decoding_from_dectet(shift_register_); + void process_index_hole() { + index_count_++; + } + + std::shared_ptr get_sector(const uint8_t sector) { + const uint16_t sector_address = uint16_t((track_ << 8) | sector); + if(sector_cache_[sector_address]) return sector_cache_[sector_address]; + + const std::shared_ptr first_sector = get_next_sector(); + if(!first_sector) return first_sector; + if(first_sector->sector == sector) return first_sector; + + while(1) { + const std::shared_ptr next_sector = get_next_sector(); + if(next_sector->sector == first_sector->sector) return nullptr; + if(next_sector->sector == sector) return next_sector; } + } - void proceed_to_shift_value(unsigned int shift_value) { - const int max_index_count = index_count_ + 2; - while(shift_register_ != shift_value && index_count_ < max_index_count) { - run_for(Cycles(1)); - } - } - - void process_index_hole() { - index_count_++; - } - - std::shared_ptr get_sector(uint8_t sector) { - const uint16_t sector_address = uint16_t((track_ << 8) | sector); - if(sector_cache_[sector_address]) return sector_cache_[sector_address]; - - const std::shared_ptr first_sector = get_next_sector(); - if(!first_sector) return first_sector; - if(first_sector->sector == sector) return first_sector; + std::shared_ptr get_next_sector() { + auto sector = std::make_shared(); + const int max_index_count = index_count_ + 2; + while(index_count_ < max_index_count) { + // look for a sector header while(1) { - const std::shared_ptr next_sector = get_next_sector(); - if(next_sector->sector == first_sector->sector) return nullptr; - if(next_sector->sector == sector) return next_sector; + if(proceed_to_next_block(max_index_count) == 0x08) break; + if(index_count_ >= max_index_count) return nullptr; + } + + // get sector details, skip if this looks malformed + uint8_t checksum = uint8_t(get_next_byte()); + sector->sector = uint8_t(get_next_byte()); + sector->track = uint8_t(get_next_byte()); + uint8_t disk_id[2]; + disk_id[0] = uint8_t(get_next_byte()); + disk_id[1] = uint8_t(get_next_byte()); + if(checksum != (sector->sector ^ sector->track ^ disk_id[0] ^ disk_id[1])) continue; + + // look for the following data + while(1) { + if(proceed_to_next_block(max_index_count) == 0x07) break; + if(index_count_ >= max_index_count) return nullptr; + } + + checksum = 0; + for(std::size_t c = 0; c < 256; c++) { + sector->data[c] = uint8_t(get_next_byte()); + checksum ^= sector->data[c]; + } + + if(checksum == get_next_byte()) { + uint16_t sector_address = uint16_t((sector->track << 8) | sector->sector); + sector_cache_[sector_address] = sector; + return sector; } } - std::shared_ptr get_next_sector() { - auto sector = std::make_shared(); - const int max_index_count = index_count_ + 2; - - while(index_count_ < max_index_count) { - // look for a sector header - while(1) { - if(proceed_to_next_block(max_index_count) == 0x08) break; - if(index_count_ >= max_index_count) return nullptr; - } - - // get sector details, skip if this looks malformed - uint8_t checksum = uint8_t(get_next_byte()); - sector->sector = uint8_t(get_next_byte()); - sector->track = uint8_t(get_next_byte()); - uint8_t disk_id[2]; - disk_id[0] = uint8_t(get_next_byte()); - disk_id[1] = uint8_t(get_next_byte()); - if(checksum != (sector->sector ^ sector->track ^ disk_id[0] ^ disk_id[1])) continue; - - // look for the following data - while(1) { - if(proceed_to_next_block(max_index_count) == 0x07) break; - if(index_count_ >= max_index_count) return nullptr; - } - - checksum = 0; - for(std::size_t c = 0; c < 256; c++) { - sector->data[c] = uint8_t(get_next_byte()); - checksum ^= sector->data[c]; - } - - if(checksum == get_next_byte()) { - uint16_t sector_address = uint16_t((sector->track << 8) | sector->sector); - sector_cache_[sector_address] = sector; - return sector; - } - } - - return nullptr; - } + return nullptr; + } }; std::vector Analyser::Static::Commodore::GetFiles(const std::shared_ptr &disk) { @@ -181,7 +181,7 @@ std::vector Analyser::Static::Commodore::GetFiles(const std::shared_ptr directory; uint8_t next_track = 18; uint8_t next_sector = 1; - while(1) { + while(true) { sector = parser.sector(next_track, next_sector); if(!sector) break; directory.insert(directory.end(), sector->data.begin(), sector->data.end()); @@ -216,7 +216,9 @@ std::vector Analyser::Static::Commodore::GetFiles(const std::shared_ptr Analyser::Static::Commodore::GetFiles(const std::shared_ptrdata[2]) | uint16_t(sector->data[3] << 8); if(next_track) - new_file.data.insert(new_file.data.end(), sector->data.begin() + (is_first_sector ? 4 : 2), sector->data.end()); + new_file.data.insert( + new_file.data.end(), + sector->data.begin() + (is_first_sector ? 4 : 2), + sector->data.end() + ); else - new_file.data.insert(new_file.data.end(), sector->data.begin() + 2, sector->data.begin() + next_sector); + new_file.data.insert( + new_file.data.end(), + sector->data.begin() + 2, + sector->data.begin() + next_sector + ); is_first_sector = false; } diff --git a/Analyser/Static/Commodore/Disk.hpp b/Analyser/Static/Commodore/Disk.hpp index a29da4266..ae9bc4187 100644 --- a/Analyser/Static/Commodore/Disk.hpp +++ b/Analyser/Static/Commodore/Disk.hpp @@ -15,6 +15,6 @@ namespace Analyser::Static::Commodore { -std::vector GetFiles(const std::shared_ptr &disk); +std::vector GetFiles(const std::shared_ptr &); } diff --git a/Analyser/Static/Commodore/StaticAnalyser.cpp b/Analyser/Static/Commodore/StaticAnalyser.cpp index 5635b1d5b..34a4bb30b 100644 --- a/Analyser/Static/Commodore/StaticAnalyser.cpp +++ b/Analyser/Static/Commodore/StaticAnalyser.cpp @@ -22,7 +22,7 @@ using namespace Analyser::Static::Commodore; static std::vector> - Vic20CartridgesFrom(const std::vector> &cartridges) { +Vic20CartridgesFrom(const std::vector> &cartridges) { std::vector> vic20_cartridges; for(const auto &cartridge : cartridges) { @@ -42,7 +42,11 @@ static std::vector> return vic20_cartridges; } -Analyser::Static::TargetList Analyser::Static::Commodore::GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType) { +Analyser::Static::TargetList Analyser::Static::Commodore::GetTargets( + const Media &media, + const std::string &file_name, + TargetPlatform::IntType +) { TargetList destination; auto target = std::make_unique(); @@ -93,7 +97,8 @@ Analyser::Static::TargetList Analyser::Static::Commodore::GetTargets(const Media // make a first guess based on loading address switch(files.front().starting_address) { default: - Log::Logger().error().append("Unrecognised loading address for Commodore program: %04x", files.front().starting_address); + Log::Logger().error().append( + "Unrecognised loading address for Commodore program: %04x", files.front().starting_address); [[fallthrough]]; case 0x1001: memory_model = Target::MemoryModel::Unexpanded; @@ -142,7 +147,8 @@ Analyser::Static::TargetList Analyser::Static::Commodore::GetTargets(const Media // region 0x0400 to 0x1000 is touched and this is an unexpanded machine, mark as 3kb. // if(starting_address + file_size > 0x2000) // target->memory_model = Target::MemoryModel::ThirtyTwoKB; -// else if(target->memory_model == Target::MemoryModel::Unexpanded && !(starting_address >= 0x1000 || starting_address+file_size < 0x0400)) +// else if(target->memory_model == Target::MemoryModel::Unexpanded && +// !(starting_address >= 0x1000 || starting_address+file_size < 0x0400)) // target->memory_model = Target::MemoryModel::ThirtyTwoKB; // } // } diff --git a/Analyser/Static/Commodore/StaticAnalyser.hpp b/Analyser/Static/Commodore/StaticAnalyser.hpp index ed4091c83..47488824e 100644 --- a/Analyser/Static/Commodore/StaticAnalyser.hpp +++ b/Analyser/Static/Commodore/StaticAnalyser.hpp @@ -14,6 +14,6 @@ namespace Analyser::Static::Commodore { -TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms); +TargetList GetTargets(const Media &, const std::string &file_name, TargetPlatform::IntType potential_platforms); } diff --git a/Analyser/Static/Commodore/Tape.cpp b/Analyser/Static/Commodore/Tape.cpp index cbcd18e65..b7891614d 100644 --- a/Analyser/Static/Commodore/Tape.cpp +++ b/Analyser/Static/Commodore/Tape.cpp @@ -55,7 +55,9 @@ std::vector Analyser::Static::Commodore::GetFiles(const std::shared_ptrstarting_address; new_file.ending_address = header->ending_address; new_file.data.swap(data->data); - new_file.type = (header->type == Storage::Tape::Commodore::Header::RelocatableProgram) ? File::RelocatableProgram : File::NonRelocatableProgram; + new_file.type = + header->type == Storage::Tape::Commodore::Header::RelocatableProgram + ? File::RelocatableProgram : File::NonRelocatableProgram; file_list.push_back(new_file); } diff --git a/Analyser/Static/Commodore/Tape.hpp b/Analyser/Static/Commodore/Tape.hpp index 278924bf6..d0ddb9b3a 100644 --- a/Analyser/Static/Commodore/Tape.hpp +++ b/Analyser/Static/Commodore/Tape.hpp @@ -13,6 +13,6 @@ namespace Analyser::Static::Commodore { -std::vector GetFiles(const std::shared_ptr &tape); +std::vector GetFiles(const std::shared_ptr &); } diff --git a/Analyser/Static/Disassembler/6502.cpp b/Analyser/Static/Disassembler/6502.cpp index 039f7e2c7..37e39d056 100644 --- a/Analyser/Static/Disassembler/6502.cpp +++ b/Analyser/Static/Disassembler/6502.cpp @@ -17,7 +17,12 @@ using PartialDisassembly = Analyser::Static::Disassembly::PartialDisassembly &memory, const std::function &address_mapper, uint16_t entry_point) { +static void AddToDisassembly( + PartialDisassembly &disassembly, + const std::vector &memory, + const std::function &address_mapper, + uint16_t entry_point +) { disassembly.disassembly.internal_calls.insert(entry_point); uint16_t address = entry_point; while(true) { @@ -75,23 +80,25 @@ static void AddToDisassembly(PartialDisassembly &disassembly, const std::vector< } // Decode operation. -#define RM_INSTRUCTION(base, op) \ - case base+0x09: case base+0x05: case base+0x15: case base+0x01: case base+0x11: case base+0x0d: case base+0x1d: case base+0x19: \ - instruction.operation = op; \ +#define RM_INSTRUCTION(base, op) \ + case base+0x09: case base+0x05: case base+0x15: case base+0x01: \ + case base+0x11: case base+0x0d: case base+0x1d: case base+0x19: \ + instruction.operation = op; \ break; -#define URM_INSTRUCTION(base, op) \ +#define URM_INSTRUCTION(base, op) \ case base+0x07: case base+0x17: case base+0x03: case base+0x13: case base+0x0f: case base+0x1f: case base+0x1b: \ - instruction.operation = op; \ + instruction.operation = op; \ break; -#define M_INSTRUCTION(base, op) \ +#define M_INSTRUCTION(base, op) \ case base+0x0a: case base+0x06: case base+0x16: case base+0x0e: case base+0x1e: \ - instruction.operation = op; \ + instruction.operation = op; \ break; -#define IM_INSTRUCTION(base, op) \ +#define IM_INSTRUCTION(base, op) \ case base: instruction.operation = op; break; + switch(operation) { default: instruction.operation = Instruction::KIL; @@ -259,7 +266,10 @@ static void AddToDisassembly(PartialDisassembly &disassembly, const std::vector< disassembly.disassembly.instructions_by_address[instruction.address] = instruction; // TODO: something wider-ranging than this - if(instruction.addressing_mode == Instruction::Absolute || instruction.addressing_mode == Instruction::ZeroPage) { + if( + instruction.addressing_mode == Instruction::Absolute || + instruction.addressing_mode == Instruction::ZeroPage + ) { const size_t mapped_address = address_mapper(instruction.operand); const bool is_external = mapped_address >= memory.size(); @@ -272,20 +282,23 @@ static void AddToDisassembly(PartialDisassembly &disassembly, const std::vector< case Instruction::ADC: case Instruction::SBC: case Instruction::LAS: case Instruction::CMP: case Instruction::CPX: case Instruction::CPY: - (is_external ? disassembly.disassembly.external_loads : disassembly.disassembly.internal_loads).insert(instruction.operand); + (is_external ? disassembly.disassembly.external_loads : disassembly.disassembly.internal_loads) + .insert(instruction.operand); break; case Instruction::STY: case Instruction::STX: case Instruction::STA: case Instruction::AXS: case Instruction::AHX: case Instruction::SHX: case Instruction::SHY: case Instruction::TAS: - (is_external ? disassembly.disassembly.external_stores : disassembly.disassembly.internal_stores).insert(instruction.operand); + (is_external ? disassembly.disassembly.external_stores : disassembly.disassembly.internal_stores) + .insert(instruction.operand); break; case Instruction::SLO: case Instruction::RLA: case Instruction::SRE: case Instruction::RRA: case Instruction::DCP: case Instruction::ISC: case Instruction::INC: case Instruction::DEC: case Instruction::ASL: case Instruction::ROL: case Instruction::LSR: case Instruction::ROR: - (is_external ? disassembly.disassembly.external_modifies : disassembly.disassembly.internal_modifies).insert(instruction.operand); + (is_external ? disassembly.disassembly.external_modifies : disassembly.disassembly.internal_modifies) + .insert(instruction.operand); break; } } @@ -330,5 +343,10 @@ Disassembly Analyser::Static::MOS6502::Disassemble( const std::vector &memory, const std::function &address_mapper, std::vector entry_points) { - return Analyser::Static::Disassembly::Disassemble(memory, address_mapper, entry_points, false); + return Analyser::Static::Disassembly::Disassemble( + memory, + address_mapper, + entry_points, + false + ); } diff --git a/Analyser/Static/Disassembler/AddressMapper.hpp b/Analyser/Static/Disassembler/AddressMapper.hpp index c3ddb02b6..4cef4088f 100644 --- a/Analyser/Static/Disassembler/AddressMapper.hpp +++ b/Analyser/Static/Disassembler/AddressMapper.hpp @@ -16,7 +16,7 @@ namespace Analyser::Static::Disassembler { Provides an address mapper that relocates a chunk of memory so that it starts at address @c start_address. */ -template std::function OffsetMapper(T start_address) { +template std::function OffsetMapper(const T start_address) { return [start_address](T argument) { return size_t(argument - start_address); }; diff --git a/Analyser/Static/Disassembler/Z80.cpp b/Analyser/Static/Disassembler/Z80.cpp index c35adbab1..9b1193125 100644 --- a/Analyser/Static/Disassembler/Z80.cpp +++ b/Analyser/Static/Disassembler/Z80.cpp @@ -16,44 +16,48 @@ namespace { using PartialDisassembly = Analyser::Static::Disassembly::PartialDisassembly; class Accessor { - public: - Accessor(const std::vector &memory, const std::function &address_mapper, uint16_t address) : - memory_(memory), address_mapper_(address_mapper), address_(address) {} +public: + Accessor( + const std::vector &memory, + const std::function &address_mapper, + uint16_t address + ) : + memory_(memory), address_mapper_(address_mapper), address_(address) {} - uint8_t byte() { - std::size_t mapped_address = address_mapper_(address_); - address_++; - if(mapped_address >= memory_.size()) { - overrun_ = true; - return 0xff; - } - return memory_[mapped_address]; + uint8_t byte() { + std::size_t mapped_address = address_mapper_(address_); + ++address_; + if(mapped_address >= memory_.size()) { + overrun_ = true; + return 0xff; } + return memory_[mapped_address]; + } - uint16_t word() { - uint8_t low = byte(); - uint8_t high = byte(); - return uint16_t(low | (high << 8)); - } + uint16_t word() { + uint8_t low = byte(); + uint8_t high = byte(); + return uint16_t(low | (high << 8)); + } - bool overrun() { - return overrun_; - } + bool overrun() const { + return overrun_; + } - bool at_end() { - std::size_t mapped_address = address_mapper_(address_); - return mapped_address >= memory_.size(); - } + bool at_end() const { + std::size_t mapped_address = address_mapper_(address_); + return mapped_address >= memory_.size(); + } - uint16_t address() { - return address_; - } + uint16_t address() const { + return address_; + } - private: - const std::vector &memory_; - const std::function &address_mapper_; - uint16_t address_; - bool overrun_ = false; +private: + const std::vector &memory_; + const std::function &address_mapper_; + uint16_t address_; + bool overrun_ = false; }; constexpr uint8_t x(uint8_t v) { return v >> 6; } @@ -83,8 +87,12 @@ Instruction::Location register_pair_table2[] = { Instruction::Location::AF }; -Instruction::Location RegisterTableEntry(int offset, Accessor &accessor, Instruction &instruction, bool needs_indirect_offset) { - Instruction::Location register_table[] = { +Instruction::Location RegisterTableEntry( + const int offset, Accessor &accessor, + Instruction &instruction, + const bool needs_indirect_offset +) { + constexpr Instruction::Location register_table[] = { Instruction::Location::B, Instruction::Location::C, Instruction::Location::D, Instruction::Location::E, Instruction::Location::H, Instruction::Location::L, @@ -92,7 +100,7 @@ Instruction::Location RegisterTableEntry(int offset, Accessor &accessor, Instruc Instruction::Location::A }; - Instruction::Location location = register_table[offset]; + const Instruction::Location location = register_table[offset]; if(location == Instruction::Location::HL_Indirect && needs_indirect_offset) { instruction.offset = accessor.byte() - 128; } @@ -100,7 +108,7 @@ Instruction::Location RegisterTableEntry(int offset, Accessor &accessor, Instruc return location; } -Instruction::Operation alu_table[] = { +constexpr Instruction::Operation alu_table[] = { Instruction::Operation::ADD, Instruction::Operation::ADC, Instruction::Operation::SUB, @@ -111,7 +119,7 @@ Instruction::Operation alu_table[] = { Instruction::Operation::CP }; -Instruction::Operation rotation_table[] = { +constexpr Instruction::Operation rotation_table[] = { Instruction::Operation::RLC, Instruction::Operation::RRC, Instruction::Operation::RL, @@ -122,19 +130,32 @@ Instruction::Operation rotation_table[] = { Instruction::Operation::SRL }; -Instruction::Operation block_table[][4] = { - {Instruction::Operation::LDI, Instruction::Operation::CPI, Instruction::Operation::INI, Instruction::Operation::OUTI}, - {Instruction::Operation::LDD, Instruction::Operation::CPD, Instruction::Operation::IND, Instruction::Operation::OUTD}, - {Instruction::Operation::LDIR, Instruction::Operation::CPIR, Instruction::Operation::INIR, Instruction::Operation::OTIR}, - {Instruction::Operation::LDDR, Instruction::Operation::CPDR, Instruction::Operation::INDR, Instruction::Operation::OTDR}, +constexpr Instruction::Operation block_table[][4] = { + { + Instruction::Operation::LDI, Instruction::Operation::CPI, + Instruction::Operation::INI, Instruction::Operation::OUTI + }, + { + Instruction::Operation::LDD, Instruction::Operation::CPD, + Instruction::Operation::IND, Instruction::Operation::OUTD + }, + { + Instruction::Operation::LDIR, Instruction::Operation::CPIR, + Instruction::Operation::INIR, Instruction::Operation::OTIR + }, + { + Instruction::Operation::LDDR, Instruction::Operation::CPDR, + Instruction::Operation::INDR, Instruction::Operation::OTDR + }, }; -void DisassembleCBPage(Accessor &accessor, Instruction &instruction, bool needs_indirect_offset) { +void DisassembleCBPage(Accessor &accessor, Instruction &instruction, const bool needs_indirect_offset) { const uint8_t operation = accessor.byte(); if(!x(operation)) { instruction.operation = rotation_table[y(operation)]; - instruction.source = instruction.destination = RegisterTableEntry(z(operation), accessor, instruction, needs_indirect_offset); + instruction.source = instruction.destination = + RegisterTableEntry(z(operation), accessor, instruction, needs_indirect_offset); } else { instruction.destination = RegisterTableEntry(z(operation), accessor, instruction, needs_indirect_offset); instruction.source = Instruction::Location::Operand; @@ -148,7 +169,7 @@ void DisassembleCBPage(Accessor &accessor, Instruction &instruction, bool needs_ } } -void DisassembleEDPage(Accessor &accessor, Instruction &instruction, bool needs_indirect_offset) { +void DisassembleEDPage(Accessor &accessor, Instruction &instruction, const bool needs_indirect_offset) { const uint8_t operation = accessor.byte(); switch(x(operation)) { @@ -170,7 +191,8 @@ void DisassembleEDPage(Accessor &accessor, Instruction &instruction, bool needs_ if(y(operation) == 6) { instruction.destination = Instruction::Location::None; } else { - instruction.destination = RegisterTableEntry(y(operation), accessor, instruction, needs_indirect_offset); + instruction.destination = + RegisterTableEntry(y(operation), accessor, instruction, needs_indirect_offset); } break; case 1: @@ -179,7 +201,8 @@ void DisassembleEDPage(Accessor &accessor, Instruction &instruction, bool needs_ if(y(operation) == 6) { instruction.source = Instruction::Location::None; } else { - instruction.source = RegisterTableEntry(y(operation), accessor, instruction, needs_indirect_offset); + instruction.source = + RegisterTableEntry(y(operation), accessor, instruction, needs_indirect_offset); } break; case 2: @@ -190,11 +213,13 @@ void DisassembleEDPage(Accessor &accessor, Instruction &instruction, bool needs_ case 3: instruction.operation = Instruction::Operation::LD; if(q(operation)) { - instruction.destination = RegisterTableEntry(p(operation), accessor, instruction, needs_indirect_offset); + instruction.destination = + RegisterTableEntry(p(operation), accessor, instruction, needs_indirect_offset); instruction.source = Instruction::Location::Operand_Indirect; } else { instruction.destination = Instruction::Location::Operand_Indirect; - instruction.source = RegisterTableEntry(p(operation), accessor, instruction, needs_indirect_offset); + instruction.source = + RegisterTableEntry(p(operation), accessor, instruction, needs_indirect_offset); } instruction.operand = accessor.word(); break; @@ -202,7 +227,8 @@ void DisassembleEDPage(Accessor &accessor, Instruction &instruction, bool needs_ instruction.operation = Instruction::Operation::NEG; break; case 5: - instruction.operation = (y(operation) == 1) ? Instruction::Operation::RETI : Instruction::Operation::RETN; + instruction.operation = + y(operation) == 1 ? Instruction::Operation::RETI : Instruction::Operation::RETN; break; case 6: instruction.operation = Instruction::Operation::IM; @@ -253,7 +279,7 @@ void DisassembleMainPage(Accessor &accessor, Instruction &instruction) { } hl_substitution = None; while(true) { - uint8_t operation = accessor.byte(); + const uint8_t operation = accessor.byte(); switch(x(operation)) { case 0: @@ -343,15 +369,18 @@ void DisassembleMainPage(Accessor &accessor, Instruction &instruction) { break; case 4: instruction.operation = Instruction::Operation::INC; - instruction.source = instruction.destination = RegisterTableEntry(y(operation), accessor, instruction, needs_indirect_offset); + instruction.source = instruction.destination = + RegisterTableEntry(y(operation), accessor, instruction, needs_indirect_offset); break; case 5: instruction.operation = Instruction::Operation::DEC; - instruction.source = instruction.destination = RegisterTableEntry(y(operation), accessor, instruction, needs_indirect_offset); + instruction.source = instruction.destination = + RegisterTableEntry(y(operation), accessor, instruction, needs_indirect_offset); break; case 6: instruction.operation = Instruction::Operation::LD; - instruction.destination = RegisterTableEntry(y(operation), accessor, instruction, needs_indirect_offset); + instruction.destination = + RegisterTableEntry(y(operation), accessor, instruction, needs_indirect_offset); instruction.source = Instruction::Location::Operand; instruction.operand = accessor.byte(); break; @@ -374,8 +403,10 @@ void DisassembleMainPage(Accessor &accessor, Instruction &instruction) { instruction.operation = Instruction::Operation::HALT; } else { instruction.operation = Instruction::Operation::LD; - instruction.source = RegisterTableEntry(z(operation), accessor, instruction, needs_indirect_offset); - instruction.destination = RegisterTableEntry(y(operation), accessor, instruction, needs_indirect_offset); + instruction.source = + RegisterTableEntry(z(operation), accessor, instruction, needs_indirect_offset); + instruction.destination = + RegisterTableEntry(y(operation), accessor, instruction, needs_indirect_offset); } break; case 2: @@ -517,10 +548,14 @@ void DisassembleMainPage(Accessor &accessor, Instruction &instruction) { instruction.destination == Instruction::Location::HL_Indirect) { if(instruction.source == Instruction::Location::HL_Indirect) { - instruction.source = (hl_substitution == IX) ? Instruction::Location::IX_Indirect_Offset : Instruction::Location::IY_Indirect_Offset; + instruction.source = + hl_substitution == IX ? + Instruction::Location::IX_Indirect_Offset : Instruction::Location::IY_Indirect_Offset; } if(instruction.destination == Instruction::Location::HL_Indirect) { - instruction.destination = (hl_substitution == IX) ? Instruction::Location::IX_Indirect_Offset : Instruction::Location::IY_Indirect_Offset; + instruction.destination = + hl_substitution == IX ? + Instruction::Location::IX_Indirect_Offset : Instruction::Location::IY_Indirect_Offset; } return; } @@ -542,7 +577,12 @@ void DisassembleMainPage(Accessor &accessor, Instruction &instruction) { } struct Z80Disassembler { - static void AddToDisassembly(PartialDisassembly &disassembly, const std::vector &memory, const std::function &address_mapper, uint16_t entry_point) { + static void AddToDisassembly( + PartialDisassembly &disassembly, + const std::vector &memory, + const std::function &address_mapper, + const uint16_t entry_point + ) { disassembly.disassembly.internal_calls.insert(entry_point); Accessor accessor(memory, address_mapper, entry_point); diff --git a/Analyser/Static/DiskII/StaticAnalyser.cpp b/Analyser/Static/DiskII/StaticAnalyser.cpp index 14f8f3d9d..3da62f061 100644 --- a/Analyser/Static/DiskII/StaticAnalyser.cpp +++ b/Analyser/Static/DiskII/StaticAnalyser.cpp @@ -47,7 +47,11 @@ Analyser::Static::Target *OricTarget(const Storage::Encodings::AppleGCR::Sector } -Analyser::Static::TargetList Analyser::Static::DiskII::GetTargets(const Media &media, const std::string &, TargetPlatform::IntType) { +Analyser::Static::TargetList Analyser::Static::DiskII::GetTargets( + const Media &media, + const std::string &, + TargetPlatform::IntType +) { // This analyser can comprehend disks only. if(media.disks.empty()) return {}; @@ -62,7 +66,8 @@ Analyser::Static::TargetList Analyser::Static::DiskII::GetTargets(const Media &m } // Grab track 0, sector 0: the boot sector. - const auto track_zero = disk->get_track_at_position(Storage::Disk::Track::Address(0, Storage::Disk::HeadPosition(0))); + const auto track_zero = + disk->get_track_at_position(Storage::Disk::Track::Address(0, Storage::Disk::HeadPosition(0))); const auto sector_map = Storage::Encodings::AppleGCR::sectors_from_segment( Storage::Disk::track_serialisation(*track_zero, Storage::Time(1, 50000))); @@ -89,7 +94,8 @@ Analyser::Static::TargetList Analyser::Static::DiskII::GetTargets(const Media &m // If the boot sector looks like it's intended for the Oric, create an Oric. // Otherwise go with the Apple II. - const auto disassembly = Analyser::Static::MOS6502::Disassemble(sector_zero->data, Analyser::Static::Disassembler::OffsetMapper(0xb800), {0xb800}); + const auto disassembly = Analyser::Static::MOS6502::Disassemble( + sector_zero->data, Analyser::Static::Disassembler::OffsetMapper(0xb800), {0xb800}); bool did_read_shift_register = false; bool is_oric = false; diff --git a/Analyser/Static/DiskII/StaticAnalyser.hpp b/Analyser/Static/DiskII/StaticAnalyser.hpp index 9f1da10d8..68f22062b 100644 --- a/Analyser/Static/DiskII/StaticAnalyser.hpp +++ b/Analyser/Static/DiskII/StaticAnalyser.hpp @@ -14,6 +14,6 @@ namespace Analyser::Static::DiskII { -TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms); +TargetList GetTargets(const Media &, const std::string &file_name, TargetPlatform::IntType potential_platforms); } diff --git a/Analyser/Static/Enterprise/StaticAnalyser.cpp b/Analyser/Static/Enterprise/StaticAnalyser.cpp index 02a3166f0..d7d64eae4 100644 --- a/Analyser/Static/Enterprise/StaticAnalyser.cpp +++ b/Analyser/Static/Enterprise/StaticAnalyser.cpp @@ -26,7 +26,11 @@ bool insensitive_equal(const std::string &lhs, const std::string &rhs) { } -Analyser::Static::TargetList Analyser::Static::Enterprise::GetTargets(const Media &media, const std::string &, TargetPlatform::IntType) { +Analyser::Static::TargetList Analyser::Static::Enterprise::GetTargets( + const Media &media, + const std::string &, + TargetPlatform::IntType +) { // This analyser can comprehend disks only. if(media.disks.empty()) return {}; @@ -72,7 +76,8 @@ Analyser::Static::TargetList Analyser::Static::Enterprise::GetTargets(const Medi if(!has_exdos_ini) { if(did_pick_file) { - target->loading_command = std::string("run \"") + selected_file->name + "." + selected_file->extension + "\"\n"; + target->loading_command = + std::string("run \"") + selected_file->name + "." + selected_file->extension + "\"\n"; } else { target->loading_command = ":dir\n"; } diff --git a/Analyser/Static/Enterprise/StaticAnalyser.hpp b/Analyser/Static/Enterprise/StaticAnalyser.hpp index c88aa43c3..52ba54b6e 100644 --- a/Analyser/Static/Enterprise/StaticAnalyser.hpp +++ b/Analyser/Static/Enterprise/StaticAnalyser.hpp @@ -14,6 +14,6 @@ namespace Analyser::Static::Enterprise { -TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms); +TargetList GetTargets(const Media &, const std::string &file_name, TargetPlatform::IntType potential_platforms); } diff --git a/Analyser/Static/FAT12/StaticAnalyser.cpp b/Analyser/Static/FAT12/StaticAnalyser.cpp index dfca8053e..35db3c70e 100644 --- a/Analyser/Static/FAT12/StaticAnalyser.cpp +++ b/Analyser/Static/FAT12/StaticAnalyser.cpp @@ -16,7 +16,11 @@ #include "../../../Storage/Disk/Encodings/MFM/SegmentParser.hpp" -Analyser::Static::TargetList Analyser::Static::FAT12::GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType platforms) { +Analyser::Static::TargetList Analyser::Static::FAT12::GetTargets( + const Media &media, + const std::string &file_name, + TargetPlatform::IntType platforms +) { // This analyser can comprehend disks only. if(media.disks.empty()) return {}; @@ -39,7 +43,8 @@ Analyser::Static::TargetList Analyser::Static::FAT12::GetTargets(const Media &me } // Attempt to grab MFM track 0, sector 1: the boot sector. - const auto track_zero = disk->get_track_at_position(Storage::Disk::Track::Address(0, Storage::Disk::HeadPosition(0))); + const auto track_zero = + disk->get_track_at_position(Storage::Disk::Track::Address(0, Storage::Disk::HeadPosition(0))); const auto sector_map = Storage::Encodings::MFM::sectors_from_segment( Storage::Disk::track_serialisation( *track_zero, diff --git a/Analyser/Static/FAT12/StaticAnalyser.hpp b/Analyser/Static/FAT12/StaticAnalyser.hpp index 52e8e538a..7d5fdef6f 100644 --- a/Analyser/Static/FAT12/StaticAnalyser.hpp +++ b/Analyser/Static/FAT12/StaticAnalyser.hpp @@ -14,6 +14,6 @@ namespace Analyser::Static::FAT12 { -TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms); +TargetList GetTargets(const Media &, const std::string &file_name, TargetPlatform::IntType potential_platforms); } diff --git a/Analyser/Static/MSX/Cartridge.hpp b/Analyser/Static/MSX/Cartridge.hpp index a5436d483..92c1264fe 100644 --- a/Analyser/Static/MSX/Cartridge.hpp +++ b/Analyser/Static/MSX/Cartridge.hpp @@ -26,7 +26,7 @@ struct Cartridge: public ::Storage::Cartridge::Cartridge { }; const Type type; - Cartridge(const std::vector &segments, Type type) : + Cartridge(const std::vector &segments, const Type type) : Storage::Cartridge::Cartridge(segments), type(type) {} }; diff --git a/Analyser/Static/MSX/StaticAnalyser.cpp b/Analyser/Static/MSX/StaticAnalyser.cpp index 2b8f6415d..7460200b2 100644 --- a/Analyser/Static/MSX/StaticAnalyser.cpp +++ b/Analyser/Static/MSX/StaticAnalyser.cpp @@ -27,7 +27,8 @@ static std::unique_ptr CartridgeTarget( std::vector output_segments; if(segment.data.size() & 0x1fff) { std::vector truncated_data; - std::vector::difference_type truncated_size = std::vector::difference_type(segment.data.size()) & ~0x1fff; + const auto truncated_size = + std::vector::difference_type(segment.data.size()) & ~0x1fff; truncated_data.insert(truncated_data.begin(), segment.data.begin(), segment.data.begin() + truncated_size); output_segments.emplace_back(start_address, truncated_data); } else { @@ -82,7 +83,7 @@ static Analyser::Static::TargetList CartridgeTargetsFrom( if(segments.size() != 1) continue; // Which must be no more than 63 bytes larger than a multiple of 8 kb in size. - Storage::Cartridge::Cartridge::Segment segment = segments.front(); + const Storage::Cartridge::Cartridge::Segment &segment = segments.front(); const size_t data_size = segment.data.size(); if(data_size < 0x2000 || (data_size & 0x1fff) > 64) continue; @@ -101,7 +102,7 @@ static Analyser::Static::TargetList CartridgeTargetsFrom( // Reject cartridge if the ROM header wasn't found. if(!found_start) continue; - uint16_t init_address = uint16_t(segment.data[2] | (segment.data[3] << 8)); + const uint16_t init_address = uint16_t(segment.data[2] | (segment.data[3] << 8)); // TODO: check for a rational init address? // If this ROM is less than 48kb in size then it's an ordinary ROM. Just emplace it and move on. @@ -137,10 +138,12 @@ static Analyser::Static::TargetList CartridgeTargetsFrom( } // Weight confidences by number of observed hits; if any is above 60% confidence, just use it. - const auto ascii_8kb_total = address_counts[0x6000] + address_counts[0x6800] + address_counts[0x7000] + address_counts[0x7800]; + const auto ascii_8kb_total = + address_counts[0x6000] + address_counts[0x6800] + address_counts[0x7000] + address_counts[0x7800]; const auto ascii_16kb_total = address_counts[0x6000] + address_counts[0x7000] + address_counts[0x77ff]; const auto konami_total = address_counts[0x6000] + address_counts[0x8000] + address_counts[0xa000]; - const auto konami_with_scc_total = address_counts[0x5000] + address_counts[0x7000] + address_counts[0x9000] + address_counts[0xb000]; + const auto konami_with_scc_total = + address_counts[0x5000] + address_counts[0x7000] + address_counts[0x9000] + address_counts[0xb000]; const auto total_hits = ascii_8kb_total + ascii_16kb_total + konami_total + konami_with_scc_total; @@ -182,7 +185,11 @@ static Analyser::Static::TargetList CartridgeTargetsFrom( return targets; } -Analyser::Static::TargetList Analyser::Static::MSX::GetTargets(const Media &media, const std::string &, TargetPlatform::IntType) { +Analyser::Static::TargetList Analyser::Static::MSX::GetTargets( + const Media &media, + const std::string &, + TargetPlatform::IntType +) { TargetList destination; // Append targets for any cartridges that look correct. @@ -194,7 +201,7 @@ Analyser::Static::TargetList Analyser::Static::MSX::GetTargets(const Media &medi // Check tapes for loadable files. for(auto &tape : media.tapes) { - std::vector files_on_tape = GetFiles(tape); + const std::vector files_on_tape = GetFiles(tape); if(!files_on_tape.empty()) { switch(files_on_tape.front().type) { case File::Type::ASCII: target->loading_command = "RUN\"CAS:\r"; break; diff --git a/Analyser/Static/MSX/StaticAnalyser.hpp b/Analyser/Static/MSX/StaticAnalyser.hpp index 1fa329dd7..a8da5d157 100644 --- a/Analyser/Static/MSX/StaticAnalyser.hpp +++ b/Analyser/Static/MSX/StaticAnalyser.hpp @@ -14,6 +14,6 @@ namespace Analyser::Static::MSX { -TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms); +TargetList GetTargets(const Media &, const std::string &file_name, TargetPlatform::IntType potential_platforms); } diff --git a/Analyser/Static/MSX/Tape.hpp b/Analyser/Static/MSX/Tape.hpp index 47bc1810d..abcae17f1 100644 --- a/Analyser/Static/MSX/Tape.hpp +++ b/Analyser/Static/MSX/Tape.hpp @@ -32,6 +32,6 @@ struct File { File(); }; -std::vector GetFiles(const std::shared_ptr &tape); +std::vector GetFiles(const std::shared_ptr &); } diff --git a/Analyser/Static/Macintosh/StaticAnalyser.cpp b/Analyser/Static/Macintosh/StaticAnalyser.cpp index 94356ef98..348693b31 100644 --- a/Analyser/Static/Macintosh/StaticAnalyser.cpp +++ b/Analyser/Static/Macintosh/StaticAnalyser.cpp @@ -9,7 +9,11 @@ #include "StaticAnalyser.hpp" #include "Target.hpp" -Analyser::Static::TargetList Analyser::Static::Macintosh::GetTargets(const Media &media, const std::string &, TargetPlatform::IntType) { +Analyser::Static::TargetList Analyser::Static::Macintosh::GetTargets( + const Media &media, + const std::string &, + TargetPlatform::IntType +) { // This analyser can comprehend disks and mass-storage devices only. if(media.disks.empty() && media.mass_storage_devices.empty()) return {}; diff --git a/Analyser/Static/Macintosh/StaticAnalyser.hpp b/Analyser/Static/Macintosh/StaticAnalyser.hpp index bf8924246..6d16cec99 100644 --- a/Analyser/Static/Macintosh/StaticAnalyser.hpp +++ b/Analyser/Static/Macintosh/StaticAnalyser.hpp @@ -14,6 +14,6 @@ namespace Analyser::Static::Macintosh { -TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms); +TargetList GetTargets(const Media &, const std::string &file_name, TargetPlatform::IntType potential_platforms); } diff --git a/Analyser/Static/Oric/StaticAnalyser.cpp b/Analyser/Static/Oric/StaticAnalyser.cpp index 273c01e9f..4c72d3349 100644 --- a/Analyser/Static/Oric/StaticAnalyser.cpp +++ b/Analyser/Static/Oric/StaticAnalyser.cpp @@ -22,12 +22,22 @@ using namespace Analyser::Static::Oric; namespace { -int score(const Analyser::Static::MOS6502::Disassembly &disassembly, const std::set &rom_functions, const std::set &variable_locations) { +int score( + const Analyser::Static::MOS6502::Disassembly &disassembly, + const std::set &rom_functions, + const std::set &variable_locations +) { int score = 0; - for(const auto address : disassembly.outward_calls) score += (rom_functions.find(address) != rom_functions.end()) ? 1 : -1; - for(const auto address : disassembly.external_stores) score += (variable_locations.find(address) != variable_locations.end()) ? 1 : -1; - for(const auto address : disassembly.external_loads) score += (variable_locations.find(address) != variable_locations.end()) ? 1 : -1; + for(const auto address : disassembly.outward_calls) { + score += (rom_functions.find(address) != rom_functions.end()) ? 1 : -1; + } + for(const auto address : disassembly.external_stores) { + score += (variable_locations.find(address) != variable_locations.end()) ? 1 : -1; + } + for(const auto address : disassembly.external_loads) { + score += (variable_locations.find(address) != variable_locations.end()) ? 1 : -1; + } return score; } @@ -35,19 +45,32 @@ int score(const Analyser::Static::MOS6502::Disassembly &disassembly, const std:: int basic10_score(const Analyser::Static::MOS6502::Disassembly &disassembly) { const std::set rom_functions = { 0x0228, 0x022b, - 0xc3ca, 0xc3f8, 0xc448, 0xc47c, 0xc4b5, 0xc4e3, 0xc4e0, 0xc524, 0xc56f, 0xc5a2, 0xc5f8, 0xc60a, 0xc6a5, 0xc6de, 0xc719, 0xc738, - 0xc773, 0xc824, 0xc832, 0xc841, 0xc8c1, 0xc8fe, 0xc91f, 0xc93f, 0xc941, 0xc91e, 0xc98b, 0xc996, 0xc9b3, 0xc9e0, 0xca0a, 0xca1c, - 0xca1f, 0xca3e, 0xca61, 0xca78, 0xca98, 0xcad2, 0xcb61, 0xcb9f, 0xcc59, 0xcbed, 0xcc0a, 0xcc8c, 0xcc8f, 0xccba, 0xccc9, 0xccfd, - 0xce0c, 0xce77, 0xce8b, 0xcfac, 0xcf74, 0xd03c, 0xd059, 0xcff0, 0xd087, 0xd0f2, 0xd0fc, 0xd361, 0xd3eb, 0xd47e, 0xd4a6, 0xd401, - 0xd593, 0xd5a3, 0xd4fa, 0xd595, 0xd730, 0xd767, 0xd816, 0xd82a, 0xd856, 0xd861, 0xd8a6, 0xd8b5, 0xd80a, 0xd867, 0xd938, 0xd894, - 0xd89d, 0xd8ac, 0xd983, 0xd993, 0xd9b5, 0xd93d, 0xd965, 0xda3f, 0xd9c6, 0xda16, 0xdaab, 0xdada, 0xda6b, 0xdb92, 0xdbb9, 0xdc79, - 0xdd4d, 0xdda3, 0xddbf, 0xd0d0, 0xde77, 0xdef4, 0xdf0b, 0xdf0f, 0xdf04, 0xdf12, 0xdf31, 0xdf4c, 0xdf8c, 0xdfa5, 0xdfcf, 0xe076, - 0xe0c1, 0xe22a, 0xe27c, 0xe2a6, 0xe313, 0xe34b, 0xe387, 0xe38e, 0xe3d7, 0xe407, 0xe43b, 0xe46f, 0xe4a8, 0xe4f2, 0xe554, 0xe57d, - 0xe585, 0xe58c, 0xe594, 0xe5a4, 0xe5ab, 0xe5b6, 0xe5ea, 0xe563, 0xe5c6, 0xe630, 0xe696, 0xe6ba, 0xe6ca, 0xe725, 0xe7aa, 0xe903, - 0xe7db, 0xe80d, 0xe987, 0xe9d1, 0xe87d, 0xe905, 0xe965, 0xe974, 0xe994, 0xe9a9, 0xe9bb, 0xec45, 0xeccc, 0xedc4, 0xecc7, 0xed01, - 0xed09, 0xed70, 0xed81, 0xed8f, 0xe0ad, 0xeee8, 0xeef8, 0xebdf, 0xebe2, 0xebe5, 0xebeb, 0xebee, 0xebf4, 0xebf7, 0xebfa, 0xebe8, - 0xf43c, 0xf4ef, 0xf523, 0xf561, 0xf535, 0xf57b, 0xf5d3, 0xf71a, 0xf73f, 0xf7e4, 0xf7e0, 0xf82f, 0xf88f, 0xf8af, 0xf8b5, 0xf920, - 0xf967, 0xf960, 0xf9c9, 0xfa14, 0xfa85, 0xfa9b, 0xfab1, 0xfac7, 0xfafa, 0xfb10, 0xfb26, 0xfbb6, 0xfbfe + 0xc3ca, 0xc3f8, 0xc448, 0xc47c, 0xc4b5, 0xc4e3, 0xc4e0, 0xc524, + 0xc56f, 0xc5a2, 0xc5f8, 0xc60a, 0xc6a5, 0xc6de, 0xc719, 0xc738, + 0xc773, 0xc824, 0xc832, 0xc841, 0xc8c1, 0xc8fe, 0xc91f, 0xc93f, + 0xc941, 0xc91e, 0xc98b, 0xc996, 0xc9b3, 0xc9e0, 0xca0a, 0xca1c, + 0xca1f, 0xca3e, 0xca61, 0xca78, 0xca98, 0xcad2, 0xcb61, 0xcb9f, + 0xcc59, 0xcbed, 0xcc0a, 0xcc8c, 0xcc8f, 0xccba, 0xccc9, 0xccfd, + 0xce0c, 0xce77, 0xce8b, 0xcfac, 0xcf74, 0xd03c, 0xd059, 0xcff0, + 0xd087, 0xd0f2, 0xd0fc, 0xd361, 0xd3eb, 0xd47e, 0xd4a6, 0xd401, + 0xd593, 0xd5a3, 0xd4fa, 0xd595, 0xd730, 0xd767, 0xd816, 0xd82a, + 0xd856, 0xd861, 0xd8a6, 0xd8b5, 0xd80a, 0xd867, 0xd938, 0xd894, + 0xd89d, 0xd8ac, 0xd983, 0xd993, 0xd9b5, 0xd93d, 0xd965, 0xda3f, + 0xd9c6, 0xda16, 0xdaab, 0xdada, 0xda6b, 0xdb92, 0xdbb9, 0xdc79, + 0xdd4d, 0xdda3, 0xddbf, 0xd0d0, 0xde77, 0xdef4, 0xdf0b, 0xdf0f, + 0xdf04, 0xdf12, 0xdf31, 0xdf4c, 0xdf8c, 0xdfa5, 0xdfcf, 0xe076, + 0xe0c1, 0xe22a, 0xe27c, 0xe2a6, 0xe313, 0xe34b, 0xe387, 0xe38e, + 0xe3d7, 0xe407, 0xe43b, 0xe46f, 0xe4a8, 0xe4f2, 0xe554, 0xe57d, + 0xe585, 0xe58c, 0xe594, 0xe5a4, 0xe5ab, 0xe5b6, 0xe5ea, 0xe563, + 0xe5c6, 0xe630, 0xe696, 0xe6ba, 0xe6ca, 0xe725, 0xe7aa, 0xe903, + 0xe7db, 0xe80d, 0xe987, 0xe9d1, 0xe87d, 0xe905, 0xe965, 0xe974, + 0xe994, 0xe9a9, 0xe9bb, 0xec45, 0xeccc, 0xedc4, 0xecc7, 0xed01, + 0xed09, 0xed70, 0xed81, 0xed8f, 0xe0ad, 0xeee8, 0xeef8, 0xebdf, + 0xebe2, 0xebe5, 0xebeb, 0xebee, 0xebf4, 0xebf7, 0xebfa, 0xebe8, + 0xf43c, 0xf4ef, 0xf523, 0xf561, 0xf535, 0xf57b, 0xf5d3, 0xf71a, + 0xf73f, 0xf7e4, 0xf7e0, 0xf82f, 0xf88f, 0xf8af, 0xf8b5, 0xf920, + 0xf967, 0xf960, 0xf9c9, 0xfa14, 0xfa85, 0xfa9b, 0xfab1, 0xfac7, + 0xfafa, 0xfb10, 0xfb26, 0xfbb6, 0xfbfe }; const std::set variable_locations = { 0x0228, 0x0229, 0x022a, 0x022b, 0x022c, 0x022d, 0x0230 @@ -59,19 +82,32 @@ int basic10_score(const Analyser::Static::MOS6502::Disassembly &disassembly) { int basic11_score(const Analyser::Static::MOS6502::Disassembly &disassembly) { const std::set rom_functions = { 0x0238, 0x023b, 0x023e, 0x0241, 0x0244, 0x0247, - 0xc3c6, 0xc3f4, 0xc444, 0xc47c, 0xc4a8, 0xc4d3, 0xc4e0, 0xc524, 0xc55f, 0xc592, 0xc5e8, 0xc5fa, 0xc692, 0xc6b3, 0xc6ee, 0xc70d, - 0xc748, 0xc7fd, 0xc809, 0xc816, 0xc82f, 0xc855, 0xc8c1, 0xc915, 0xc952, 0xc971, 0xc973, 0xc9a0, 0xc9bd, 0xc9c8, 0xc9e5, 0xca12, - 0xca3c, 0xca4e, 0xca51, 0xca70, 0xca99, 0xcac2, 0xcae2, 0xcb1c, 0xcbab, 0xcbf0, 0xcc59, 0xccb0, 0xccce, 0xcd16, 0xcd19, 0xcd46, - 0xcd55, 0xcd89, 0xce98, 0xcf03, 0xcf17, 0xcfac, 0xd000, 0xd03c, 0xd059, 0xd07c, 0xd113, 0xd17e, 0xd188, 0xd361, 0xd3eb, 0xd47e, - 0xd4a6, 0xd4ba, 0xd593, 0xd5a3, 0xd5b5, 0xd650, 0xd730, 0xd767, 0xd816, 0xd82a, 0xd856, 0xd861, 0xd8a6, 0xd8b5, 0xd8c5, 0xd922, - 0xd938, 0xd94f, 0xd958, 0xd967, 0xd983, 0xd993, 0xd9b5, 0xd9de, 0xda0c, 0xda3f, 0xda51, 0xdaa1, 0xdaab, 0xdada, 0xdaf6, 0xdb92, - 0xdbb9, 0xdcaf, 0xdd51, 0xdda7, 0xddc3, 0xddd4, 0xde77, 0xdef4, 0xdf0b, 0xdf0f, 0xdf13, 0xdf21, 0xdf49, 0xdf4c, 0xdf8c, 0xdfbd, - 0xdfe7, 0xe076, 0xe0c5, 0xe22e, 0xe27c, 0xe2aa, 0xe313, 0xe34f, 0xe38b, 0xe392, 0xe3db, 0xe407, 0xe43f, 0xe46f, 0xe4ac, 0xe4e0, - 0xe4f2, 0xe56c, 0xe57d, 0xe585, 0xe58c, 0xe594, 0xe5a4, 0xe5ab, 0xe5b6, 0xe5ea, 0xe5f5, 0xe607, 0xe65e, 0xe6c9, 0xe735, 0xe75a, - 0xe76a, 0xe7b2, 0xe85b, 0xe903, 0xe909, 0xe946, 0xe987, 0xe9d1, 0xeaf0, 0xeb78, 0xebce, 0xebe7, 0xec0c, 0xec21, 0xec33, 0xec45, - 0xeccc, 0xedc4, 0xede0, 0xee1a, 0xee22, 0xee8c, 0xee9d, 0xeeab, 0xeec9, 0xeee8, 0xeef8, 0xf0c8, 0xf0fd, 0xf110, 0xf11d, 0xf12d, - 0xf204, 0xf210, 0xf268, 0xf37f, 0xf495, 0xf4ef, 0xf523, 0xf561, 0xf590, 0xf5c1, 0xf602, 0xf71a, 0xf77c, 0xf7e4, 0xf816, 0xf865, - 0xf88f, 0xf8af, 0xf8b5, 0xf920, 0xf967, 0xf9aa, 0xf9c9, 0xfa14, 0xfa9f, 0xfab5, 0xfacb, 0xfae1, 0xfb14, 0xfb2a, 0xfb40, 0xfbd0, + 0xc3c6, 0xc3f4, 0xc444, 0xc47c, 0xc4a8, 0xc4d3, 0xc4e0, 0xc524, + 0xc55f, 0xc592, 0xc5e8, 0xc5fa, 0xc692, 0xc6b3, 0xc6ee, 0xc70d, + 0xc748, 0xc7fd, 0xc809, 0xc816, 0xc82f, 0xc855, 0xc8c1, 0xc915, + 0xc952, 0xc971, 0xc973, 0xc9a0, 0xc9bd, 0xc9c8, 0xc9e5, 0xca12, + 0xca3c, 0xca4e, 0xca51, 0xca70, 0xca99, 0xcac2, 0xcae2, 0xcb1c, + 0xcbab, 0xcbf0, 0xcc59, 0xccb0, 0xccce, 0xcd16, 0xcd19, 0xcd46, + 0xcd55, 0xcd89, 0xce98, 0xcf03, 0xcf17, 0xcfac, 0xd000, 0xd03c, + 0xd059, 0xd07c, 0xd113, 0xd17e, 0xd188, 0xd361, 0xd3eb, 0xd47e, + 0xd4a6, 0xd4ba, 0xd593, 0xd5a3, 0xd5b5, 0xd650, 0xd730, 0xd767, + 0xd816, 0xd82a, 0xd856, 0xd861, 0xd8a6, 0xd8b5, 0xd8c5, 0xd922, + 0xd938, 0xd94f, 0xd958, 0xd967, 0xd983, 0xd993, 0xd9b5, 0xd9de, + 0xda0c, 0xda3f, 0xda51, 0xdaa1, 0xdaab, 0xdada, 0xdaf6, 0xdb92, + 0xdbb9, 0xdcaf, 0xdd51, 0xdda7, 0xddc3, 0xddd4, 0xde77, 0xdef4, + 0xdf0b, 0xdf0f, 0xdf13, 0xdf21, 0xdf49, 0xdf4c, 0xdf8c, 0xdfbd, + 0xdfe7, 0xe076, 0xe0c5, 0xe22e, 0xe27c, 0xe2aa, 0xe313, 0xe34f, + 0xe38b, 0xe392, 0xe3db, 0xe407, 0xe43f, 0xe46f, 0xe4ac, 0xe4e0, + 0xe4f2, 0xe56c, 0xe57d, 0xe585, 0xe58c, 0xe594, 0xe5a4, 0xe5ab, + 0xe5b6, 0xe5ea, 0xe5f5, 0xe607, 0xe65e, 0xe6c9, 0xe735, 0xe75a, + 0xe76a, 0xe7b2, 0xe85b, 0xe903, 0xe909, 0xe946, 0xe987, 0xe9d1, + 0xeaf0, 0xeb78, 0xebce, 0xebe7, 0xec0c, 0xec21, 0xec33, 0xec45, + 0xeccc, 0xedc4, 0xede0, 0xee1a, 0xee22, 0xee8c, 0xee9d, 0xeeab, + 0xeec9, 0xeee8, 0xeef8, 0xf0c8, 0xf0fd, 0xf110, 0xf11d, 0xf12d, + 0xf204, 0xf210, 0xf268, 0xf37f, 0xf495, 0xf4ef, 0xf523, 0xf561, + 0xf590, 0xf5c1, 0xf602, 0xf71a, 0xf77c, 0xf7e4, 0xf816, 0xf865, + 0xf88f, 0xf8af, 0xf8b5, 0xf920, 0xf967, 0xf9aa, 0xf9c9, 0xfa14, + 0xfa9f, 0xfab5, 0xfacb, 0xfae1, 0xfb14, 0xfb2a, 0xfb40, 0xfbd0, 0xfc18 }; const std::set variable_locations = { @@ -102,7 +138,7 @@ bool is_microdisc(Storage::Encodings::MFM::Parser &parser) { return !std::memcmp(signature, first_sample.data(), sizeof(signature)); } -bool is_400_loader(Storage::Encodings::MFM::Parser &parser, uint16_t range_start, uint16_t range_end) { +bool is_400_loader(Storage::Encodings::MFM::Parser &parser, const uint16_t range_start, const uint16_t range_end) { /* Both the Jasmin and BD-DOS boot sectors are sector 1 of track 0 and are loaded at $400; use disassembly to test for likely matches. @@ -120,8 +156,8 @@ bool is_400_loader(Storage::Encodings::MFM::Parser &parser, uint16_t range_start } // Grab a disassembly. - const auto disassembly = - Analyser::Static::MOS6502::Disassemble(first_sample, Analyser::Static::Disassembler::OffsetMapper(0x400), {0x400}); + const auto disassembly = Analyser::Static::MOS6502::Disassemble( + first_sample, Analyser::Static::Disassembler::OffsetMapper(0x400), {0x400}); // Check for references to the Jasmin registers. int register_hits = 0; @@ -145,7 +181,11 @@ bool is_bd500(Storage::Encodings::MFM::Parser &parser) { } -Analyser::Static::TargetList Analyser::Static::Oric::GetTargets(const Media &media, const std::string &, TargetPlatform::IntType) { +Analyser::Static::TargetList Analyser::Static::Oric::GetTargets( + const Media &media, + const std::string &, + TargetPlatform::IntType +) { auto target = std::make_unique(); target->confidence = 0.5; @@ -160,7 +200,11 @@ Analyser::Static::TargetList Analyser::Static::Oric::GetTargets(const Media &med if(file.data_type == File::MachineCode) { std::vector entry_points = {file.starting_address}; const Analyser::Static::MOS6502::Disassembly disassembly = - Analyser::Static::MOS6502::Disassemble(file.data, Analyser::Static::Disassembler::OffsetMapper(file.starting_address), entry_points); + Analyser::Static::MOS6502::Disassemble( + file.data, + Analyser::Static::Disassembler::OffsetMapper(file.starting_address), + entry_points + ); if(basic10_score(disassembly) > basic11_score(disassembly)) ++basic10_votes; else ++basic11_votes; } diff --git a/Analyser/Static/Oric/StaticAnalyser.hpp b/Analyser/Static/Oric/StaticAnalyser.hpp index dadd90834..00dab44d8 100644 --- a/Analyser/Static/Oric/StaticAnalyser.hpp +++ b/Analyser/Static/Oric/StaticAnalyser.hpp @@ -14,6 +14,6 @@ namespace Analyser::Static::Oric { -TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms); +TargetList GetTargets(const Media &, const std::string &file_name, TargetPlatform::IntType potential_platforms); } diff --git a/Analyser/Static/Oric/Tape.hpp b/Analyser/Static/Oric/Tape.hpp index a4bafbdfb..bf6a37203 100644 --- a/Analyser/Static/Oric/Tape.hpp +++ b/Analyser/Static/Oric/Tape.hpp @@ -28,6 +28,6 @@ struct File { std::vector data; }; -std::vector GetFiles(const std::shared_ptr &tape); +std::vector GetFiles(const std::shared_ptr &); } diff --git a/Analyser/Static/PCCompatible/StaticAnalyser.cpp b/Analyser/Static/PCCompatible/StaticAnalyser.cpp index dbac1dd0b..1c5982a30 100644 --- a/Analyser/Static/PCCompatible/StaticAnalyser.cpp +++ b/Analyser/Static/PCCompatible/StaticAnalyser.cpp @@ -9,7 +9,11 @@ #include "StaticAnalyser.hpp" #include "Target.hpp" -Analyser::Static::TargetList Analyser::Static::PCCompatible::GetTargets(const Media &media, const std::string &, TargetPlatform::IntType) { +Analyser::Static::TargetList Analyser::Static::PCCompatible::GetTargets( + const Media &media, + const std::string &, + TargetPlatform::IntType +) { // This analyser can comprehend disks only. if(media.disks.empty()) return {}; diff --git a/Analyser/Static/PCCompatible/StaticAnalyser.hpp b/Analyser/Static/PCCompatible/StaticAnalyser.hpp index c7a52373e..21a9cd68d 100644 --- a/Analyser/Static/PCCompatible/StaticAnalyser.hpp +++ b/Analyser/Static/PCCompatible/StaticAnalyser.hpp @@ -14,6 +14,6 @@ namespace Analyser::Static::PCCompatible { -TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms); +TargetList GetTargets(const Media &, const std::string &file_name, TargetPlatform::IntType potential_platforms); } diff --git a/Analyser/Static/Sega/StaticAnalyser.cpp b/Analyser/Static/Sega/StaticAnalyser.cpp index 02ad18f23..038b1619b 100644 --- a/Analyser/Static/Sega/StaticAnalyser.cpp +++ b/Analyser/Static/Sega/StaticAnalyser.cpp @@ -13,7 +13,11 @@ #include #include -Analyser::Static::TargetList Analyser::Static::Sega::GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType) { +Analyser::Static::TargetList Analyser::Static::Sega::GetTargets( + const Media &media, + const std::string &file_name, + TargetPlatform::IntType +) { if(media.cartridges.empty()) return {}; @@ -54,7 +58,8 @@ Analyser::Static::TargetList Analyser::Static::Sega::GetTargets(const Media &med if(lowercase_name.find("(jp)") == std::string::npos) { target->region = (lowercase_name.find("(us)") == std::string::npos && - lowercase_name.find("(ntsc)") == std::string::npos) ? Target::Region::Europe : Target::Region::USA; + lowercase_name.find("(ntsc)") == std::string::npos) ? + Target::Region::Europe : Target::Region::USA; } } break; } @@ -63,9 +68,9 @@ Analyser::Static::TargetList Analyser::Static::Sega::GetTargets(const Media &med // If one is found, set the paging scheme appropriately. const uint16_t inverse_checksum = uint16_t(0x10000 - (data[0x7fe6] | (data[0x7fe7] << 8))); if( - data[0x7fe3] >= 0x87 && data[0x7fe3] < 0x96 && // i.e. game is dated between 1987 and 1996 + data[0x7fe3] >= 0x87 && data[0x7fe3] < 0x96 && // i.e. game is dated between 1987 and 1996 (inverse_checksum&0xff) == data[0x7fe8] && - (inverse_checksum >> 8) == data[0x7fe9] && // i.e. the standard checksum appears to be present + (inverse_checksum >> 8) == data[0x7fe9] && // i.e. the standard checksum appears to be present. !data[0x7fea] && !data[0x7feb] && !data[0x7fec] && !data[0x7fed] && !data[0x7fee] && !data[0x7fef] ) { target->paging_scheme = Target::PagingScheme::Codemasters; diff --git a/Analyser/Static/Sega/StaticAnalyser.hpp b/Analyser/Static/Sega/StaticAnalyser.hpp index 6840cefb2..ce8a37aff 100644 --- a/Analyser/Static/Sega/StaticAnalyser.hpp +++ b/Analyser/Static/Sega/StaticAnalyser.hpp @@ -14,6 +14,6 @@ namespace Analyser::Static::Sega { -TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms); +TargetList GetTargets(const Media &, const std::string &file_name, TargetPlatform::IntType potential_platforms); } diff --git a/Analyser/Static/Sega/Target.hpp b/Analyser/Static/Sega/Target.hpp index 74653f012..3b33d4d7d 100644 --- a/Analyser/Static/Sega/Target.hpp +++ b/Analyser/Static/Sega/Target.hpp @@ -45,7 +45,7 @@ struct Target: public Analyser::Static::Target, public Reflection::StructImpl= Analyser::Static::Sega::Target::Model::MasterSystem; } diff --git a/Analyser/Static/StaticAnalyser.cpp b/Analyser/Static/StaticAnalyser.cpp index d57749c1e..a48b4d5e8 100644 --- a/Analyser/Static/StaticAnalyser.cpp +++ b/Analyser/Static/StaticAnalyser.cpp @@ -104,14 +104,14 @@ std::string get_extension(const std::string &name) { } class MediaAccumulator { - public: +public: MediaAccumulator(const std::string &file_name, TargetPlatform::IntType &potential_platforms) : file_name_(file_name), potential_platforms_(potential_platforms), extension_(get_extension(file_name)) {} /// Adds @c instance to the media collection and adds @c platforms to the set of potentials. /// If @c instance is an @c TargetPlatform::TypeDistinguisher then it is given an opportunity to restrict the set of potentials. template - void insert(TargetPlatform::IntType platforms, std::shared_ptr instance) { + void insert(const TargetPlatform::IntType platforms, std::shared_ptr instance) { if constexpr (std::is_base_of_v) { media.disks.push_back(instance); } else if constexpr (std::is_base_of_v) { @@ -134,13 +134,13 @@ class MediaAccumulator { /// Concstructs a new instance of @c InstanceT supplying @c args and adds it to the back of @c list using @c insert_instance. template - void insert(TargetPlatform::IntType platforms, Args &&... args) { + void insert(const TargetPlatform::IntType platforms, Args &&... args) { insert(platforms, std::make_shared(std::forward(args)...)); } /// Calls @c insert with the specified parameters, ignoring any exceptions thrown. template - void try_insert(TargetPlatform::IntType platforms, Args &&... args) { + void try_insert(const TargetPlatform::IntType platforms, Args &&... args) { try { insert(platforms, std::forward(args)...); } catch(...) {} @@ -149,22 +149,22 @@ class MediaAccumulator { /// Performs a @c try_insert for an object of @c InstanceT if @c extension matches that of the file name, /// providing the file name as the only construction argument. template - void try_standard(TargetPlatform::IntType platforms, const char *extension) { + void try_standard(const TargetPlatform::IntType platforms, const char *extension) { if(name_matches(extension)) { try_insert(platforms, file_name_); } } - bool name_matches(const char *extension) { + bool name_matches(const char *const extension) { return extension_ == extension; } Media media; - private: - const std::string &file_name_; - TargetPlatform::IntType &potential_platforms_; - const std::string extension_; +private: + const std::string &file_name_; + TargetPlatform::IntType &potential_platforms_; + const std::string extension_; }; } @@ -227,7 +227,8 @@ static Media GetMediaAndPlatforms(const std::string &file_name, TargetPlatform:: accumulator.try_standard(TargetPlatform::AppleII, "hdv"); accumulator.try_standard>( - TargetPlatform::Acorn | TargetPlatform::AmstradCPC | TargetPlatform::Commodore | TargetPlatform::Oric | TargetPlatform::ZXSpectrum, + TargetPlatform::Acorn | TargetPlatform::AmstradCPC | TargetPlatform::Commodore | + TargetPlatform::Oric | TargetPlatform::ZXSpectrum, "hfe"); // TODO: switch to AllDisk once the MSX stops being so greedy. accumulator.try_standard>(TargetPlatform::PCCompatible, "ima"); diff --git a/Analyser/Static/ZX8081/StaticAnalyser.cpp b/Analyser/Static/ZX8081/StaticAnalyser.cpp index 5fc80d64b..9fd3c819f 100644 --- a/Analyser/Static/ZX8081/StaticAnalyser.cpp +++ b/Analyser/Static/ZX8081/StaticAnalyser.cpp @@ -28,7 +28,11 @@ static std::vector GetFiles(const std::shared_ptr files = GetFiles(media.tapes.front()); diff --git a/Analyser/Static/ZX8081/StaticAnalyser.hpp b/Analyser/Static/ZX8081/StaticAnalyser.hpp index bac9d9084..aab427387 100644 --- a/Analyser/Static/ZX8081/StaticAnalyser.hpp +++ b/Analyser/Static/ZX8081/StaticAnalyser.hpp @@ -14,6 +14,6 @@ namespace Analyser::Static::ZX8081 { -TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms); +TargetList GetTargets(const Media &, const std::string &file_name, TargetPlatform::IntType potential_platforms); } diff --git a/Analyser/Static/ZXSpectrum/StaticAnalyser.cpp b/Analyser/Static/ZXSpectrum/StaticAnalyser.cpp index e2ef0b88c..620800ffb 100644 --- a/Analyser/Static/ZXSpectrum/StaticAnalyser.cpp +++ b/Analyser/Static/ZXSpectrum/StaticAnalyser.cpp @@ -103,7 +103,11 @@ bool IsSpectrumDisk(const std::shared_ptr &disk) { } -Analyser::Static::TargetList Analyser::Static::ZXSpectrum::GetTargets(const Media &media, const std::string &, TargetPlatform::IntType) { +Analyser::Static::TargetList Analyser::Static::ZXSpectrum::GetTargets( + const Media &media, + const std::string &, + TargetPlatform::IntType +) { TargetList destination; auto target = std::make_unique(); target->confidence = 0.5; diff --git a/Analyser/Static/ZXSpectrum/StaticAnalyser.hpp b/Analyser/Static/ZXSpectrum/StaticAnalyser.hpp index 3e48fe32b..cf02aed8c 100644 --- a/Analyser/Static/ZXSpectrum/StaticAnalyser.hpp +++ b/Analyser/Static/ZXSpectrum/StaticAnalyser.hpp @@ -14,6 +14,6 @@ namespace Analyser::Static::ZXSpectrum { -TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms); +TargetList GetTargets(const Media &, const std::string &file_name, TargetPlatform::IntType potential_platforms); } diff --git a/ClockReceiver/ClockReceiver.hpp b/ClockReceiver/ClockReceiver.hpp index c6493499a..5cc945700 100644 --- a/ClockReceiver/ClockReceiver.hpp +++ b/ClockReceiver/ClockReceiver.hpp @@ -56,218 +56,225 @@ Boolean operators, but forcing callers and receivers to be explicit as to usage. */ template class WrappedInt { - public: - using IntType = int64_t; +public: + using IntType = int64_t; - forceinline constexpr WrappedInt(IntType l) noexcept : length_(l) {} - forceinline constexpr WrappedInt() noexcept : length_(0) {} + forceinline constexpr WrappedInt(IntType l) noexcept : length_(l) {} + forceinline constexpr WrappedInt() noexcept : length_(0) {} - forceinline T &operator =(const T &rhs) { - length_ = rhs.length_; - return *this; - } + forceinline T &operator =(const T &rhs) { + length_ = rhs.length_; + return *this; + } - forceinline T &operator +=(const T &rhs) { - length_ += rhs.length_; - return *static_cast(this); - } + forceinline T &operator +=(const T &rhs) { + length_ += rhs.length_; + return *static_cast(this); + } - forceinline T &operator -=(const T &rhs) { - length_ -= rhs.length_; - return *static_cast(this); - } + forceinline T &operator -=(const T &rhs) { + length_ -= rhs.length_; + return *static_cast(this); + } - forceinline T &operator ++() { - ++ length_; - return *static_cast(this); - } + forceinline T &operator ++() { + ++ length_; + return *static_cast(this); + } - forceinline T &operator ++(int) { - length_ ++; - return *static_cast(this); - } + forceinline T &operator ++(int) { + length_ ++; + return *static_cast(this); + } - forceinline T &operator --() { - -- length_; - return *static_cast(this); - } + forceinline T &operator --() { + -- length_; + return *static_cast(this); + } - forceinline T &operator --(int) { - length_ --; - return *static_cast(this); - } + forceinline T &operator --(int) { + length_ --; + return *static_cast(this); + } - forceinline T &operator *=(const T &rhs) { - length_ *= rhs.length_; - return *static_cast(this); - } + forceinline T &operator *=(const T &rhs) { + length_ *= rhs.length_; + return *static_cast(this); + } - forceinline T &operator /=(const T &rhs) { - length_ /= rhs.length_; - return *static_cast(this); - } + forceinline T &operator /=(const T &rhs) { + length_ /= rhs.length_; + return *static_cast(this); + } - forceinline T &operator %=(const T &rhs) { - length_ %= rhs.length_; - return *static_cast(this); - } + forceinline T &operator %=(const T &rhs) { + length_ %= rhs.length_; + return *static_cast(this); + } - forceinline T &operator &=(const T &rhs) { - length_ &= rhs.length_; - return *static_cast(this); - } + forceinline T &operator &=(const T &rhs) { + length_ &= rhs.length_; + return *static_cast(this); + } - forceinline constexpr T operator +(const T &rhs) const { return T(length_ + rhs.length_); } - forceinline constexpr T operator -(const T &rhs) const { return T(length_ - rhs.length_); } + forceinline constexpr T operator +(const T &rhs) const { return T(length_ + rhs.length_); } + forceinline constexpr T operator -(const T &rhs) const { return T(length_ - rhs.length_); } - forceinline constexpr T operator *(const T &rhs) const { return T(length_ * rhs.length_); } - forceinline constexpr T operator /(const T &rhs) const { return T(length_ / rhs.length_); } + forceinline constexpr T operator *(const T &rhs) const { return T(length_ * rhs.length_); } + forceinline constexpr T operator /(const T &rhs) const { return T(length_ / rhs.length_); } - forceinline constexpr T operator %(const T &rhs) const { return T(length_ % rhs.length_); } - forceinline constexpr T operator &(const T &rhs) const { return T(length_ & rhs.length_); } + forceinline constexpr T operator %(const T &rhs) const { return T(length_ % rhs.length_); } + forceinline constexpr T operator &(const T &rhs) const { return T(length_ & rhs.length_); } - forceinline constexpr T operator -() const { return T(- length_); } + forceinline constexpr T operator -() const { return T(- length_); } - forceinline constexpr bool operator <(const T &rhs) const { return length_ < rhs.length_; } - forceinline constexpr bool operator >(const T &rhs) const { return length_ > rhs.length_; } - forceinline constexpr bool operator <=(const T &rhs) const { return length_ <= rhs.length_; } - forceinline constexpr bool operator >=(const T &rhs) const { return length_ >= rhs.length_; } - forceinline constexpr bool operator ==(const T &rhs) const { return length_ == rhs.length_; } - forceinline constexpr bool operator !=(const T &rhs) const { return length_ != rhs.length_; } + forceinline constexpr bool operator <(const T &rhs) const { return length_ < rhs.length_; } + forceinline constexpr bool operator >(const T &rhs) const { return length_ > rhs.length_; } + forceinline constexpr bool operator <=(const T &rhs) const { return length_ <= rhs.length_; } + forceinline constexpr bool operator >=(const T &rhs) const { return length_ >= rhs.length_; } + forceinline constexpr bool operator ==(const T &rhs) const { return length_ == rhs.length_; } + forceinline constexpr bool operator !=(const T &rhs) const { return length_ != rhs.length_; } - forceinline constexpr bool operator !() const { return !length_; } - // bool operator () is not supported because it offers an implicit cast to int, which is prone silently to permit misuse + forceinline constexpr bool operator !() const { return !length_; } + // bool operator () is not supported because it offers an implicit cast to int, + // which is prone silently to permit misuse. - /// @returns The underlying int, converted to an integral type of your choosing, clamped to that int's range. - template forceinline constexpr Type as() const { - if constexpr (sizeof(Type) == sizeof(IntType)) { - if constexpr (std::is_same_v) { - return length_; - } else if constexpr (std::is_signed_v) { - // Both integers are the same size, but a signed result is being asked for - // from an unsigned original. - return length_ > Type(std::numeric_limits::max()) ? Type(std::numeric_limits::max()) : Type(length_); - } else { - // An unsigned result is being asked for from a signed original. - return length_ < 0 ? 0 : Type(length_); - } + /// @returns The underlying int, converted to an integral type of your choosing, clamped to that int's range. + template forceinline constexpr Type as() const { + if constexpr (sizeof(Type) == sizeof(IntType)) { + if constexpr (std::is_same_v) { + return length_; + } else if constexpr (std::is_signed_v) { + // Both integers are the same size, but a signed result is being asked for + // from an unsigned original. + return length_ > Type(std::numeric_limits::max()) ? + Type(std::numeric_limits::max()) : Type(length_); + } else { + // An unsigned result is being asked for from a signed original. + return length_ < 0 ? 0 : Type(length_); } - - const auto clamped = std::clamp(length_, IntType(std::numeric_limits::min()), IntType(std::numeric_limits::max())); - return Type(clamped); } - /// @returns The underlying int, in its native form. - forceinline constexpr IntType as_integral() const { return length_; } + const auto clamped = std::clamp( + length_, + IntType(std::numeric_limits::min()), + IntType(std::numeric_limits::max()) + ); + return Type(clamped); + } - /*! - Severs from @c this the effect of dividing by @c divisor; @c this will end up with - the value of @c this modulo @c divisor and @c divided by @c divisor is returned. - */ - template forceinline Result divide(const T &divisor) { - Result r; - static_cast(this)->fill(r, divisor); - return r; - } + /// @returns The underlying int, in its native form. + forceinline constexpr IntType as_integral() const { return length_; } - /*! - Flushes the value in @c this. The current value is returned, and the internal value - is reset to zero. - */ - template Result flush() { - // Jiggery pokery here; switching to function overloading avoids - // the namespace-level requirement for template specialisation. - Result r; - static_cast(this)->fill(r); - return r; - } + /*! + Severs from @c this the effect of dividing by @c divisor; @c this will end up with + the value of @c this modulo @c divisor and @c divided by @c divisor is returned. + */ + template forceinline Result divide(const T &divisor) { + Result r; + static_cast(this)->fill(r, divisor); + return r; + } - // operator int() is deliberately not provided, to avoid accidental subtitution of - // classes that use this template. + /*! + Flushes the value in @c this. The current value is returned, and the internal value + is reset to zero. + */ + template Result flush() { + // Jiggery pokery here; switching to function overloading avoids + // the namespace-level requirement for template specialisation. + Result r; + static_cast(this)->fill(r); + return r; + } - protected: - IntType length_; + // operator int() is deliberately not provided, to avoid accidental subtitution of + // classes that use this template. + +protected: + IntType length_; }; /// Describes an integer number of whole cycles: pairs of clock signal transitions. class Cycles: public WrappedInt { - public: - forceinline constexpr Cycles(IntType l) noexcept : WrappedInt(l) {} - forceinline constexpr Cycles() noexcept : WrappedInt() {} - forceinline static constexpr Cycles max() { - return Cycles(std::numeric_limits::max()); - } +public: + forceinline constexpr Cycles(IntType l) noexcept : WrappedInt(l) {} + forceinline constexpr Cycles() noexcept : WrappedInt() {} + forceinline static constexpr Cycles max() { + return Cycles(std::numeric_limits::max()); + } - private: - friend WrappedInt; - void fill(Cycles &result) { - result.length_ = length_; - length_ = 0; - } +private: + friend WrappedInt; + void fill(Cycles &result) { + result.length_ = length_; + length_ = 0; + } - void fill(Cycles &result, const Cycles &divisor) { - result.length_ = length_ / divisor.length_; - length_ %= divisor.length_; - } + void fill(Cycles &result, const Cycles &divisor) { + result.length_ = length_ / divisor.length_; + length_ %= divisor.length_; + } }; /// Describes an integer number of half cycles: single clock signal transitions. class HalfCycles: public WrappedInt { - public: - forceinline constexpr HalfCycles(IntType l) noexcept : WrappedInt(l) {} - forceinline constexpr HalfCycles() noexcept : WrappedInt() {} - forceinline static constexpr HalfCycles max() { - return HalfCycles(std::numeric_limits::max()); - } +public: + forceinline constexpr HalfCycles(IntType l) noexcept : WrappedInt(l) {} + forceinline constexpr HalfCycles() noexcept : WrappedInt() {} + forceinline static constexpr HalfCycles max() { + return HalfCycles(std::numeric_limits::max()); + } - forceinline constexpr HalfCycles(const Cycles &cycles) noexcept : WrappedInt(cycles.as_integral() * 2) {} + forceinline constexpr HalfCycles(const Cycles &cycles) noexcept : + WrappedInt(cycles.as_integral() * 2) {} - /// @returns The number of whole cycles completely covered by this span of half cycles. - forceinline constexpr Cycles cycles() const { - return Cycles(length_ >> 1); - } + /// @returns The number of whole cycles completely covered by this span of half cycles. + forceinline constexpr Cycles cycles() const { + return Cycles(length_ >> 1); + } - /*! - Severs from @c this the effect of dividing by @c divisor; @c this will end up with - the value of @c this modulo @c divisor . @c this divided by @c divisor is returned. - */ - forceinline Cycles divide_cycles(const Cycles &divisor) { - const HalfCycles half_divisor = HalfCycles(divisor); - const Cycles result(length_ / half_divisor.length_); - length_ %= half_divisor.length_; - return result; - } + /*! + Severs from @c this the effect of dividing by @c divisor; @c this will end up with + the value of @c this modulo @c divisor . @c this divided by @c divisor is returned. + */ + forceinline Cycles divide_cycles(const Cycles &divisor) { + const HalfCycles half_divisor = HalfCycles(divisor); + const Cycles result(length_ / half_divisor.length_); + length_ %= half_divisor.length_; + return result; + } - /*! - Equivalent to @c divide_cycles(Cycles(1)) but faster. - */ - forceinline Cycles divide_cycles() { - const Cycles result(length_ >> 1); - length_ &= 1; - return result; - } + /*! + Equivalent to @c divide_cycles(Cycles(1)) but faster. + */ + forceinline Cycles divide_cycles() { + const Cycles result(length_ >> 1); + length_ &= 1; + return result; + } - private: - friend WrappedInt; - void fill(Cycles &result) { - result = Cycles(length_ >> 1); - length_ &= 1; - } +private: + friend WrappedInt; + void fill(Cycles &result) { + result = Cycles(length_ >> 1); + length_ &= 1; + } - void fill(HalfCycles &result) { - result.length_ = length_; - length_ = 0; - } + void fill(HalfCycles &result) { + result.length_ = length_; + length_ = 0; + } - void fill(Cycles &result, const HalfCycles &divisor) { - result = Cycles(length_ / (divisor.length_ << 1)); - length_ %= (divisor.length_ << 1); - } + void fill(Cycles &result, const HalfCycles &divisor) { + result = Cycles(length_ / (divisor.length_ << 1)); + length_ %= (divisor.length_ << 1); + } - void fill(HalfCycles &result, const HalfCycles &divisor) { - result.length_ = length_ / divisor.length_; - length_ %= divisor.length_; - } + void fill(HalfCycles &result, const HalfCycles &divisor) { + result.length_ = length_ / divisor.length_; + length_ %= divisor.length_; + } }; // Create a specialisation of WrappedInt::flush for converting HalfCycles to Cycles diff --git a/ClockReceiver/ClockingHintSource.hpp b/ClockReceiver/ClockingHintSource.hpp index be88ba404..51411f56a 100644 --- a/ClockReceiver/ClockingHintSource.hpp +++ b/ClockReceiver/ClockingHintSource.hpp @@ -58,28 +58,28 @@ struct Observer { The hint provided is just that: a hint. Owners may perform ::run_for at a greater frequency. */ class Source { - public: - /// Registers @c observer as the new clocking observer. - void set_clocking_hint_observer(Observer *observer) { - observer_ = observer; - update_clocking_observer(); - } +public: + /// Registers @c observer as the new clocking observer. + void set_clocking_hint_observer(Observer *observer) { + observer_ = observer; + update_clocking_observer(); + } - /// @returns the current preferred clocking strategy. - virtual Preference preferred_clocking() const = 0; + /// @returns the current preferred clocking strategy. + virtual Preference preferred_clocking() const = 0; - private: - Observer *observer_ = nullptr; +private: + Observer *observer_ = nullptr; - protected: - /*! - Provided for subclasses; call this whenever the clocking preference might have changed. - This will notify the observer if there is one. - */ - void update_clocking_observer() { - if(!observer_) return; - observer_->set_component_prefers_clocking(this, preferred_clocking()); - } +protected: + /*! + Provided for subclasses; call this whenever the clocking preference might have changed. + This will notify the observer if there is one. + */ + void update_clocking_observer() { + if(!observer_) return; + observer_->set_component_prefers_clocking(this, preferred_clocking()); + } }; } diff --git a/ClockReceiver/DeferredQueue.hpp b/ClockReceiver/DeferredQueue.hpp index c639ea411..f303c9ceb 100644 --- a/ClockReceiver/DeferredQueue.hpp +++ b/ClockReceiver/DeferredQueue.hpp @@ -15,78 +15,79 @@ Provides the logic to insert into and traverse a list of future scheduled items. */ template class DeferredQueue { - public: - /*! - Schedules @c action to occur in @c delay units of time. - */ - void defer(TimeUnit delay, const std::function &action) { - // Apply immediately if there's no delay (or a negative delay). - if(delay <= TimeUnit(0)) { - action(); - return; +public: + /*! + Schedules @c action to occur in @c delay units of time. + */ + void defer(TimeUnit delay, const std::function &action) { + // Apply immediately if there's no delay (or a negative delay). + if(delay <= TimeUnit(0)) { + action(); + return; + } + + if(!pending_actions_.empty()) { + // Otherwise enqueue, having subtracted the delay for any preceding events, + // and subtracting from the subsequent, if any. + auto insertion_point = pending_actions_.begin(); + while(insertion_point != pending_actions_.end() && insertion_point->delay < delay) { + delay -= insertion_point->delay; + ++insertion_point; + } + if(insertion_point != pending_actions_.end()) { + insertion_point->delay -= delay; } - if(!pending_actions_.empty()) { - // Otherwise enqueue, having subtracted the delay for any preceding events, - // and subtracting from the subsequent, if any. - auto insertion_point = pending_actions_.begin(); - while(insertion_point != pending_actions_.end() && insertion_point->delay < delay) { - delay -= insertion_point->delay; - ++insertion_point; - } - if(insertion_point != pending_actions_.end()) { - insertion_point->delay -= delay; - } + pending_actions_.emplace(insertion_point, delay, action); + } else { + pending_actions_.emplace_back(delay, action); + } + } - pending_actions_.emplace(insertion_point, delay, action); + /*! + @returns The amount of time until the next enqueued action will occur, + or TimeUnit(-1) if the queue is empty. + */ + TimeUnit time_until_next_action() const { + if(pending_actions_.empty()) return TimeUnit(-1); + return pending_actions_.front().delay; + } + + /*! + Advances the queue the specified amount of time, performing any actions it reaches. + */ + void advance(TimeUnit time) { + auto erase_iterator = pending_actions_.begin(); + while(erase_iterator != pending_actions_.end()) { + erase_iterator->delay -= time; + if(erase_iterator->delay <= TimeUnit(0)) { + time = -erase_iterator->delay; + erase_iterator->action(); + ++erase_iterator; } else { - pending_actions_.emplace_back(delay, action); + break; } } - - /*! - @returns The amount of time until the next enqueued action will occur, - or TimeUnit(-1) if the queue is empty. - */ - TimeUnit time_until_next_action() const { - if(pending_actions_.empty()) return TimeUnit(-1); - return pending_actions_.front().delay; + if(erase_iterator != pending_actions_.begin()) { + pending_actions_.erase(pending_actions_.begin(), erase_iterator); } + } - /*! - Advances the queue the specified amount of time, performing any actions it reaches. - */ - void advance(TimeUnit time) { - auto erase_iterator = pending_actions_.begin(); - while(erase_iterator != pending_actions_.end()) { - erase_iterator->delay -= time; - if(erase_iterator->delay <= TimeUnit(0)) { - time = -erase_iterator->delay; - erase_iterator->action(); - ++erase_iterator; - } else { - break; - } - } - if(erase_iterator != pending_actions_.begin()) { - pending_actions_.erase(pending_actions_.begin(), erase_iterator); - } - } + /// @returns @c true if no actions are enqueued; @c false otherwise. + bool empty() const { + return pending_actions_.empty(); + } - /// @returns @c true if no actions are enqueued; @c false otherwise. - bool empty() const { - return pending_actions_.empty(); - } +private: + // The list of deferred actions. + struct DeferredAction { + TimeUnit delay; + std::function action; - private: - // The list of deferred actions. - struct DeferredAction { - TimeUnit delay; - std::function action; - - DeferredAction(TimeUnit delay, const std::function &action) : delay(delay), action(std::move(action)) {} - }; - std::vector pending_actions_; + DeferredAction(TimeUnit delay, const std::function &action) : + delay(delay), action(std::move(action)) {} + }; + std::vector pending_actions_; }; /*! @@ -117,8 +118,6 @@ template class DeferredQueuePerformer: public DeferredQueue< DeferredQueue::advance(length); target_(length); - - // TODO: optimise this to avoid the multiple std::vector deletes. Find a neat way to expose that solution, maybe? } private: diff --git a/ClockReceiver/DeferredValue.hpp b/ClockReceiver/DeferredValue.hpp index 359186c67..c2111abe6 100644 --- a/ClockReceiver/DeferredValue.hpp +++ b/ClockReceiver/DeferredValue.hpp @@ -13,33 +13,33 @@ of future values. */ template class DeferredValue { - private: - static_assert(sizeof(ValueT) <= 4); +private: + static_assert(sizeof(ValueT) <= 4); - constexpr int elements_per_uint32 = sizeof(uint32_t) / sizeof(ValueT); - constexpr int unit_shift = sizeof(ValueT) * 8; - constexpr int insert_shift = (DeferredDepth & (elements_per_uint32 - 1)) * unit_shift; - constexpr uint32_t insert_mask = ~(0xffff'ffff << insert_shift); + constexpr int elements_per_uint32 = sizeof(uint32_t) / sizeof(ValueT); + constexpr int unit_shift = sizeof(ValueT) * 8; + constexpr int insert_shift = (DeferredDepth & (elements_per_uint32 - 1)) * unit_shift; + constexpr uint32_t insert_mask = ~(0xffff'ffff << insert_shift); - std::array backlog; + std::array backlog; - public: - /// @returns the current value. - ValueT value() const { - return uint8_t(backlog[0]); +public: + /// @returns the current value. + ValueT value() const { + return uint8_t(backlog[0]); + } + + /// Advances to the next enqueued value. + void advance() { + for(size_t c = 0; c < backlog.size() - 1; c--) { + backlog[c] = (backlog[c] >> unit_shift) | (backlog[c+1] << (32 - unit_shift)); } + backlog[backlog.size() - 1] >>= unit_shift; + } - /// Advances to the next enqueued value. - void advance() { - for(size_t c = 0; c < backlog.size() - 1; c--) { - backlog[c] = (backlog[c] >> unit_shift) | (backlog[c+1] << (32 - unit_shift)); - } - backlog[backlog.size() - 1] >>= unit_shift; - } - - /// Inserts a new value, replacing whatever is currently at the end of the queue. - void insert(ValueT value) { - backlog[DeferredDepth / elements_per_uint32] = - (backlog[DeferredDepth / elements_per_uint32] & insert_mask) | (value << insert_shift); - } + /// Inserts a new value, replacing whatever is currently at the end of the queue. + void insert(const ValueT value) { + backlog[DeferredDepth / elements_per_uint32] = + (backlog[DeferredDepth / elements_per_uint32] & insert_mask) | (value << insert_shift); + } }; diff --git a/ClockReceiver/JustInTime.hpp b/ClockReceiver/JustInTime.hpp index a8482f237..209c50fc8 100644 --- a/ClockReceiver/JustInTime.hpp +++ b/ClockReceiver/JustInTime.hpp @@ -35,251 +35,254 @@ TODO: incorporate and codify AsyncJustInTimeActor. */ template class JustInTimeActor: - public ClockingHint::Observer { - private: - /*! - A std::unique_ptr deleter which causes an update_sequence_point to occur on the actor supplied - to it at construction if it implements @c next_sequence_point(). Otherwise destruction is a no-op. + public ClockingHint::Observer +{ +private: + /*! + A std::unique_ptr deleter which causes an update_sequence_point to occur on the actor supplied + to it at construction if it implements @c next_sequence_point(). Otherwise destruction is a no-op. - **Does not delete the object.** - - This is used by the -> operators below, which provide a unique pointer to the enclosed object and - update their sequence points upon its destruction — i.e. after the caller has made whatever call - or calls as were relevant to the enclosed object. - */ - class SequencePointAwareDeleter { - public: - explicit SequencePointAwareDeleter(JustInTimeActor *actor) noexcept - : actor_(actor) {} - - forceinline void operator ()(const T *const) const { - if constexpr (has_sequence_points::value) { - actor_->update_sequence_point(); - } - } - - private: - JustInTimeActor *const actor_; - }; - - // This block of SFINAE determines whether objects of type T accepts Cycles or HalfCycles. - using HalfRunFor = void (T::*const)(HalfCycles); - static uint8_t half_sig(...); - static uint16_t half_sig(HalfRunFor); - using TargetTimeScale = - std::conditional_t< - sizeof(half_sig(&T::run_for)) == sizeof(uint16_t), - HalfCycles, - Cycles>; + **Does not delete the object.** + This is used by the -> operators below, which provide a unique pointer to the enclosed object and + update their sequence points upon its destruction — i.e. after the caller has made whatever call + or calls as were relevant to the enclosed object. + */ + class SequencePointAwareDeleter { public: - /// Constructs a new JustInTimeActor using the same construction arguments as the included object. - template JustInTimeActor(Args&&... args) : object_(std::forward(args)...) { - if constexpr (std::is_base_of::value) { - object_.set_clocking_hint_observer(this); - } - } - - /// Adds time to the actor. - /// - /// @returns @c true if adding time caused a flush; @c false otherwise. - forceinline bool operator += (LocalTimeScale rhs) { - if constexpr (std::is_base_of::value) { - if(clocking_preference_ == ClockingHint::Preference::None) { - return false; - } - } - - if constexpr (multiplier != 1) { - time_since_update_ += rhs * multiplier; - } else { - time_since_update_ += rhs; - } - is_flushed_ = false; - - if constexpr (std::is_base_of::value) { - if (clocking_preference_ == ClockingHint::Preference::RealTime) { - flush(); - return true; - } - } + explicit SequencePointAwareDeleter( + JustInTimeActor *const actor) noexcept + : actor_(actor) {} + forceinline void operator ()(const T *const) const { if constexpr (has_sequence_points::value) { - time_until_event_ -= rhs * multiplier; - if(time_until_event_ <= LocalTimeScale(0)) { - time_overrun_ = time_until_event_ / divider; - flush(); - update_sequence_point(); - return true; - } - } - - return false; - } - - /// Flushes all accumulated time and returns a pointer to the included object. - /// - /// If this object provides sequence points, checks for changes to the next - /// sequence point upon deletion of the pointer. - [[nodiscard]] forceinline auto operator->() { -#ifndef NDEBUG - assert(!flush_concurrency_check_.test_and_set()); -#endif - flush(); -#ifndef NDEBUG - flush_concurrency_check_.clear(); -#endif - return std::unique_ptr(&object_, SequencePointAwareDeleter(this)); - } - - /// Acts exactly as per the standard ->, but preserves constness. - /// - /// Despite being const, this will flush the object and, if relevant, update the next sequence point. - [[nodiscard]] forceinline auto operator -> () const { - auto non_const_this = const_cast *>(this); -#ifndef NDEBUG - assert(!non_const_this->flush_concurrency_check_.test_and_set()); -#endif - non_const_this->flush(); -#ifndef NDEBUG - non_const_this->flush_concurrency_check_.clear(); -#endif - return std::unique_ptr(&object_, SequencePointAwareDeleter(non_const_this)); - } - - /// @returns a pointer to the included object, without flushing time. - [[nodiscard]] forceinline T *last_valid() { - return &object_; - } - - /// @returns a const pointer to the included object, without flushing time. - [[nodiscard]] forceinline const T *last_valid() const { - return &object_; - } - - /// @returns the amount of time since the object was last flushed, in the target time scale. - [[nodiscard]] forceinline TargetTimeScale time_since_flush() const { - if constexpr (divider == 1) { - return time_since_update_; - } - return TargetTimeScale(time_since_update_.as_integral() / divider); - } - - /// @returns the amount of time since the object was last flushed, plus the local time scale @c offset, - /// converted to the target time scale. - [[nodiscard]] forceinline TargetTimeScale time_since_flush(LocalTimeScale offset) const { - if constexpr (divider == 1) { - return time_since_update_ + offset; - } - return TargetTimeScale((time_since_update_ + offset).as_integral() / divider); - } - - /// Flushes all accumulated time. - /// - /// This does not affect this actor's record of when the next sequence point will occur. - forceinline void flush() { - if(!is_flushed_) { - did_flush_ = is_flushed_ = true; - if constexpr (divider == 1) { - const auto duration = time_since_update_.template flush(); - object_.run_for(duration); - } else { - const auto duration = time_since_update_.template divide(LocalTimeScale(divider)); - if(duration > TargetTimeScale(0)) - object_.run_for(duration); - } + actor_->update_sequence_point(); } } - /// Indicates whether a flush has occurred since the last call to did_flush(). - [[nodiscard]] forceinline bool did_flush() { - const bool did_flush = did_flush_; - did_flush_ = false; - return did_flush; - } + private: + JustInTimeActor *const actor_; + }; - /// @returns a number in the range [-max, 0] indicating the offset of the most recent sequence - /// point from the final time at the end of the += that triggered the sequence point. - [[nodiscard]] forceinline LocalTimeScale last_sequence_point_overrun() { - return time_overrun_; - } + // This block of SFINAE determines whether objects of type T accepts Cycles or HalfCycles. + using HalfRunFor = void (T::*const)(HalfCycles); + static uint8_t half_sig(...); + static uint16_t half_sig(HalfRunFor); + using TargetTimeScale = + std::conditional_t< + sizeof(half_sig(&T::run_for)) == sizeof(uint16_t), + HalfCycles, + Cycles>; - /// @returns the number of cycles until the next sequence-point-based flush, if the embedded object - /// supports sequence points; @c LocalTimeScale() otherwise. - [[nodiscard]] LocalTimeScale cycles_until_implicit_flush() const { - return time_until_event_ / divider; +public: + /// Constructs a new JustInTimeActor using the same construction arguments as the included object. + template JustInTimeActor(Args&&... args) : object_(std::forward(args)...) { + if constexpr (std::is_base_of::value) { + object_.set_clocking_hint_observer(this); } + } - /// Indicates whether a sequence-point-caused flush will occur if the specified period is added. - [[nodiscard]] forceinline bool will_flush(LocalTimeScale rhs) const { - if constexpr (!has_sequence_points::value) { + /// Adds time to the actor. + /// + /// @returns @c true if adding time caused a flush; @c false otherwise. + forceinline bool operator += (LocalTimeScale rhs) { + if constexpr (std::is_base_of::value) { + if(clocking_preference_ == ClockingHint::Preference::None) { return false; } - return rhs >= time_until_event_; } - /// Indicates the amount of time, in the local time scale, until the first local slot that falls wholly - /// after @c duration, if that delay were to occur in @c offset units of time from now. - [[nodiscard]] forceinline LocalTimeScale back_map(TargetTimeScale duration, TargetTimeScale offset) const { - // A 1:1 mapping is easy. - if constexpr (multiplier == 1 && divider == 1) { - return duration; + if constexpr (multiplier != 1) { + time_since_update_ += rhs * multiplier; + } else { + time_since_update_ += rhs; + } + is_flushed_ = false; + + if constexpr (std::is_base_of::value) { + if (clocking_preference_ == ClockingHint::Preference::RealTime) { + flush(); + return true; } - - // Work out when this query is placed, and the time to which it relates - const auto base = time_since_update_ + offset * divider; - const auto target = base + duration * divider; - - // Figure out the number of whole input steps that is required to get - // past target, and subtract the number of whole input steps necessary - // to get to base. - const auto steps_to_base = base.as_integral() / multiplier; - const auto steps_to_target = (target.as_integral() + divider - 1) / multiplier; - - return LocalTimeScale(steps_to_target - steps_to_base); } - /// Updates this template's record of the next sequence point. - void update_sequence_point() { - if constexpr (has_sequence_points::value) { - // Keep a fast path where no conversions will be applied; if conversions are - // going to be applied then do a direct max -> max translation rather than - // allowing the arithmetic to overflow. - if constexpr (divider == 1 && std::is_same_v) { - time_until_event_ = object_.next_sequence_point(); + if constexpr (has_sequence_points::value) { + time_until_event_ -= rhs * multiplier; + if(time_until_event_ <= LocalTimeScale(0)) { + time_overrun_ = time_until_event_ / divider; + flush(); + update_sequence_point(); + return true; + } + } + + return false; + } + + /// Flushes all accumulated time and returns a pointer to the included object. + /// + /// If this object provides sequence points, checks for changes to the next + /// sequence point upon deletion of the pointer. + [[nodiscard]] forceinline auto operator->() { +#ifndef NDEBUG + assert(!flush_concurrency_check_.test_and_set()); +#endif + flush(); +#ifndef NDEBUG + flush_concurrency_check_.clear(); +#endif + return std::unique_ptr(&object_, SequencePointAwareDeleter(this)); + } + + /// Acts exactly as per the standard ->, but preserves constness. + /// + /// Despite being const, this will flush the object and, if relevant, update the next sequence point. + [[nodiscard]] forceinline auto operator -> () const { + auto non_const_this = const_cast *>(this); +#ifndef NDEBUG + assert(!non_const_this->flush_concurrency_check_.test_and_set()); +#endif + non_const_this->flush(); +#ifndef NDEBUG + non_const_this->flush_concurrency_check_.clear(); +#endif + return std::unique_ptr(&object_, SequencePointAwareDeleter(non_const_this)); + } + + /// @returns a pointer to the included object, without flushing time. + [[nodiscard]] forceinline T *last_valid() { + return &object_; + } + + /// @returns a const pointer to the included object, without flushing time. + [[nodiscard]] forceinline const T *last_valid() const { + return &object_; + } + + /// @returns the amount of time since the object was last flushed, in the target time scale. + [[nodiscard]] forceinline TargetTimeScale time_since_flush() const { + if constexpr (divider == 1) { + return time_since_update_; + } + return TargetTimeScale(time_since_update_.as_integral() / divider); + } + + /// @returns the amount of time since the object was last flushed, plus the local time scale @c offset, + /// converted to the target time scale. + [[nodiscard]] forceinline TargetTimeScale time_since_flush(LocalTimeScale offset) const { + if constexpr (divider == 1) { + return time_since_update_ + offset; + } + return TargetTimeScale((time_since_update_ + offset).as_integral() / divider); + } + + /// Flushes all accumulated time. + /// + /// This does not affect this actor's record of when the next sequence point will occur. + forceinline void flush() { + if(!is_flushed_) { + did_flush_ = is_flushed_ = true; + if constexpr (divider == 1) { + const auto duration = time_since_update_.template flush(); + object_.run_for(duration); + } else { + const auto duration = time_since_update_.template divide(LocalTimeScale(divider)); + if(duration > TargetTimeScale(0)) + object_.run_for(duration); + } + } + } + + /// Indicates whether a flush has occurred since the last call to did_flush(). + [[nodiscard]] forceinline bool did_flush() { + const bool did_flush = did_flush_; + did_flush_ = false; + return did_flush; + } + + /// @returns a number in the range [-max, 0] indicating the offset of the most recent sequence + /// point from the final time at the end of the += that triggered the sequence point. + [[nodiscard]] forceinline LocalTimeScale last_sequence_point_overrun() { + return time_overrun_; + } + + /// @returns the number of cycles until the next sequence-point-based flush, if the embedded object + /// supports sequence points; @c LocalTimeScale() otherwise. + [[nodiscard]] LocalTimeScale cycles_until_implicit_flush() const { + return time_until_event_ / divider; + } + + /// Indicates whether a sequence-point-caused flush will occur if the specified period is added. + [[nodiscard]] forceinline bool will_flush(LocalTimeScale rhs) const { + if constexpr (!has_sequence_points::value) { + return false; + } + return rhs >= time_until_event_; + } + + /// Indicates the amount of time, in the local time scale, until the first local slot that falls wholly + /// after @c duration, if that delay were to occur in @c offset units of time from now. + [[nodiscard]] forceinline LocalTimeScale back_map(TargetTimeScale duration, TargetTimeScale offset) const { + // A 1:1 mapping is easy. + if constexpr (multiplier == 1 && divider == 1) { + return duration; + } + + // Work out when this query is placed, and the time to which it relates + const auto base = time_since_update_ + offset * divider; + const auto target = base + duration * divider; + + // Figure out the number of whole input steps that is required to get + // past target, and subtract the number of whole input steps necessary + // to get to base. + const auto steps_to_base = base.as_integral() / multiplier; + const auto steps_to_target = (target.as_integral() + divider - 1) / multiplier; + + return LocalTimeScale(steps_to_target - steps_to_base); + } + + /// Updates this template's record of the next sequence point. + void update_sequence_point() { + if constexpr (has_sequence_points::value) { + // Keep a fast path where no conversions will be applied; if conversions are + // going to be applied then do a direct max -> max translation rather than + // allowing the arithmetic to overflow. + if constexpr (divider == 1 && std::is_same_v) { + time_until_event_ = object_.next_sequence_point(); + } else { + const auto time = object_.next_sequence_point(); + if(time == TargetTimeScale::max()) { + time_until_event_ = LocalTimeScale::max(); } else { - const auto time = object_.next_sequence_point(); - if(time == TargetTimeScale::max()) { - time_until_event_ = LocalTimeScale::max(); - } else { - time_until_event_ = time * divider; - } + time_until_event_ = time * divider; } - assert(time_until_event_ > LocalTimeScale(0)); } + assert(time_until_event_ > LocalTimeScale(0)); } + } - /// @returns A cached copy of the object's clocking preference. - ClockingHint::Preference clocking_preference() const { - return clocking_preference_; - } + /// @returns A cached copy of the object's clocking preference. + ClockingHint::Preference clocking_preference() const { + return clocking_preference_; + } - private: - T object_; - LocalTimeScale time_since_update_, time_until_event_, time_overrun_; - bool is_flushed_ = true; - bool did_flush_ = false; +private: + T object_; + LocalTimeScale time_since_update_, time_until_event_, time_overrun_; + bool is_flushed_ = true; + bool did_flush_ = false; - template struct has_sequence_points : std::false_type {}; - template struct has_sequence_points().next_sequence_point()))> : std::true_type {}; + template struct has_sequence_points : std::false_type {}; + template + struct has_sequence_points().next_sequence_point()))> : std::true_type {}; - ClockingHint::Preference clocking_preference_ = ClockingHint::Preference::JustInTime; - void set_component_prefers_clocking(ClockingHint::Source *, ClockingHint::Preference clocking) { - clocking_preference_ = clocking; - } + ClockingHint::Preference clocking_preference_ = ClockingHint::Preference::JustInTime; + void set_component_prefers_clocking(ClockingHint::Source *, ClockingHint::Preference clocking) { + clocking_preference_ = clocking; + } #ifndef NDEBUG - std::atomic_flag flush_concurrency_check_{}; + std::atomic_flag flush_concurrency_check_{}; #endif }; @@ -288,49 +291,50 @@ template class AsyncJustInTimeActor { - public: - /// Constructs a new AsyncJustInTimeActor using the same construction arguments as the included object. - template AsyncJustInTimeActor(TargetTimeScale threshold, Args&&... args) : - object_(std::forward(args)...), - threshold_(threshold) {} +template +class AsyncJustInTimeActor { +public: + /// Constructs a new AsyncJustInTimeActor using the same construction arguments as the included object. + template AsyncJustInTimeActor(TargetTimeScale threshold, Args&&... args) : + object_(std::forward(args)...), + threshold_(threshold) {} - /// Adds time to the actor. - inline void operator += (const LocalTimeScale &rhs) { - time_since_update_ += rhs; - if(time_since_update_ >= threshold_) { - time_since_update_ -= threshold_; - task_queue_.enqueue([this] () { - object_.run_for(threshold_); - }); - } - is_flushed_ = false; + /// Adds time to the actor. + inline void operator += (const LocalTimeScale &rhs) { + time_since_update_ += rhs; + if(time_since_update_ >= threshold_) { + time_since_update_ -= threshold_; + task_queue_.enqueue([this] () { + object_.run_for(threshold_); + }); } + is_flushed_ = false; + } - /// Flushes all accumulated time and returns a pointer to the included object. - inline T *operator->() { - flush(); - return &object_; + /// Flushes all accumulated time and returns a pointer to the included object. + inline T *operator->() { + flush(); + return &object_; + } + + /// Returns a pointer to the included object without flushing time. + inline T *last_valid() { + return &object_; + } + + /// Flushes all accumulated time. + inline void flush() { + if(!is_flushed_) { + task_queue_.flush(); + object_.run_for(time_since_update_.template flush()); + is_flushed_ = true; } + } - /// Returns a pointer to the included object without flushing time. - inline T *last_valid() { - return &object_; - } - - /// Flushes all accumulated time. - inline void flush() { - if(!is_flushed_) { - task_queue_.flush(); - object_.run_for(time_since_update_.template flush()); - is_flushed_ = true; - } - } - - private: - T object_; - LocalTimeScale time_since_update_; - TargetTimeScale threshold_; - bool is_flushed_ = true; - Concurrency::AsyncTaskQueue task_queue_; +private: + T object_; + LocalTimeScale time_since_update_; + TargetTimeScale threshold_; + bool is_flushed_ = true; + Concurrency::AsyncTaskQueue task_queue_; }; diff --git a/ClockReceiver/ScanSynchroniser.hpp b/ClockReceiver/ScanSynchroniser.hpp index c4f002d7b..031207762 100644 --- a/ClockReceiver/ScanSynchroniser.hpp +++ b/ClockReceiver/ScanSynchroniser.hpp @@ -21,65 +21,65 @@ namespace Time { of time, to bring it into phase. */ class ScanSynchroniser { - public: - /*! - @returns @c true if the emulated machine can be synchronised with the host frame output based on its - current @c [scan]status and the host machine's @c frame_duration; @c false otherwise. - */ - bool can_synchronise(const Outputs::Display::ScanStatus &scan_status, double frame_duration) { - ratio_ = 1.0; - if(scan_status.field_duration_gradient < 0.00001) { - // Check out the machine's current frame time. - // If it's within 3% of a non-zero integer multiple of the - // display rate, mark this time window to be split over the sync. - ratio_ = (frame_duration * base_multiplier_) / scan_status.field_duration; - const double integer_ratio = round(ratio_); - if(integer_ratio > 0.0) { - ratio_ /= integer_ratio; - return ratio_ <= maximum_rate_adjustment && ratio_ >= 1.0 / maximum_rate_adjustment; - } +public: + /*! + @returns @c true if the emulated machine can be synchronised with the host frame output based on its + current @c [scan]status and the host machine's @c frame_duration; @c false otherwise. + */ + bool can_synchronise(const Outputs::Display::ScanStatus &scan_status, const double frame_duration) { + ratio_ = 1.0; + if(scan_status.field_duration_gradient < 0.00001) { + // Check out the machine's current frame time. + // If it's within 3% of a non-zero integer multiple of the + // display rate, mark this time window to be split over the sync. + ratio_ = (frame_duration * base_multiplier_) / scan_status.field_duration; + const double integer_ratio = round(ratio_); + if(integer_ratio > 0.0) { + ratio_ /= integer_ratio; + return ratio_ <= maximum_rate_adjustment && ratio_ >= 1.0 / maximum_rate_adjustment; } - return false; } + return false; + } - /*! - @returns The appropriate speed multiplier for the next frame based on the inputs previously supplied to @c can_synchronise. - Results are undefined if @c can_synchroise returned @c false. - */ - double next_speed_multiplier(const Outputs::Display::ScanStatus &scan_status) { - // The host versus emulated ratio is calculated based on the current perceived frame duration of the machine. - // Either that number is exactly correct or it's already the result of some sort of low-pass filter. So there's - // no benefit to second guessing it here — just take it to be correct. - // - // ... with one slight caveat, which is that it is desireable to adjust phase here, to align vertical sync points. - // So the set speed multiplier may be adjusted slightly to aim for that. - double speed_multiplier = 1.0 / (ratio_ / base_multiplier_); - if(scan_status.current_position > 0.0) { - if(scan_status.current_position < 0.5) speed_multiplier /= phase_adjustment_ratio; - else speed_multiplier *= phase_adjustment_ratio; - } - speed_multiplier_ = (speed_multiplier_ * 0.95) + (speed_multiplier * 0.05); - return speed_multiplier_ * base_multiplier_; + /*! + @returns The appropriate speed multiplier for the next frame based on the inputs previously supplied to @c can_synchronise. + Results are undefined if @c can_synchroise returned @c false. + */ + double next_speed_multiplier(const Outputs::Display::ScanStatus &scan_status) { + // The host versus emulated ratio is calculated based on the current perceived frame duration of the machine. + // Either that number is exactly correct or it's already the result of some sort of low-pass filter. So there's + // no benefit to second guessing it here — just take it to be correct. + // + // ... with one slight caveat, which is that it is desireable to adjust phase here, to align vertical sync points. + // So the set speed multiplier may be adjusted slightly to aim for that. + double speed_multiplier = 1.0 / (ratio_ / base_multiplier_); + if(scan_status.current_position > 0.0) { + if(scan_status.current_position < 0.5) speed_multiplier /= phase_adjustment_ratio; + else speed_multiplier *= phase_adjustment_ratio; } + speed_multiplier_ = (speed_multiplier_ * 0.95) + (speed_multiplier * 0.05); + return speed_multiplier_ * base_multiplier_; + } - void set_base_speed_multiplier(double multiplier) { - base_multiplier_ = multiplier; - } + void set_base_speed_multiplier(const double multiplier) { + base_multiplier_ = multiplier; + } - double get_base_speed_multiplier() { - return base_multiplier_; - } + double get_base_speed_multiplier() const { + return base_multiplier_; + } - private: - static constexpr double maximum_rate_adjustment = 1.03; - static constexpr double phase_adjustment_ratio = 1.005; +private: + static constexpr double maximum_rate_adjustment = 1.03; + static constexpr double phase_adjustment_ratio = 1.005; - // Managed local state. - double speed_multiplier_ = 1.0; - double base_multiplier_ = 1.0; + // Managed local state. + double speed_multiplier_ = 1.0; + double base_multiplier_ = 1.0; - // Temporary storage to bridge the can_synchronise -> next_speed_multiplier gap. - double ratio_ = 1.0; + // Temporary storage to bridge the can_synchronise -> next_speed_multiplier gap. + double ratio_ = 1.0; }; } diff --git a/ClockReceiver/TimeTypes.hpp b/ClockReceiver/TimeTypes.hpp index 118649b51..1f7b1cc69 100644 --- a/ClockReceiver/TimeTypes.hpp +++ b/ClockReceiver/TimeTypes.hpp @@ -16,7 +16,9 @@ typedef double Seconds; typedef int64_t Nanos; inline Nanos nanos_now() { - return std::chrono::duration_cast(std::chrono::high_resolution_clock::now().time_since_epoch()).count(); + return std::chrono::duration_cast( + std::chrono::high_resolution_clock::now().time_since_epoch() + ).count(); } inline Seconds seconds(Nanos nanos) { diff --git a/ClockReceiver/VSyncPredictor.hpp b/ClockReceiver/VSyncPredictor.hpp index e77639a76..74e7d1176 100644 --- a/ClockReceiver/VSyncPredictor.hpp +++ b/ClockReceiver/VSyncPredictor.hpp @@ -21,132 +21,132 @@ namespace Time { (iii) optionally, timer jitter; in order to suggest when you should next start drawing. */ class VSyncPredictor { - public: - /*! - Announces to the predictor that the work of producing an output frame has begun. - */ - void begin_redraw() { - redraw_begin_time_ = nanos_now(); +public: + /*! + Announces to the predictor that the work of producing an output frame has begun. + */ + void begin_redraw() { + redraw_begin_time_ = nanos_now(); + } + + /*! + Announces to the predictor that the work of producing an output frame has ended; + the predictor will use the amount of time between each begin/end pair to modify + its expectations as to how long it takes to draw a frame. + */ + void end_redraw() { + redraw_period_.post(nanos_now() - redraw_begin_time_); + } + + /*! + Informs the predictor that a block-on-vsync has just ended, i.e. that the moment this + machine calls retrace is now. The predictor uses these notifications to estimate output + frame rate. + */ + void announce_vsync() { + const auto now = nanos_now(); + + if(last_vsync_) { + last_vsync_ += frame_duration_; + vsync_jitter_.post(last_vsync_ - now); + last_vsync_ = (last_vsync_ + now) >> 1; + } else { + last_vsync_ = now; } + } - /*! - Announces to the predictor that the work of producing an output frame has ended; - the predictor will use the amount of time between each begin/end pair to modify - its expectations as to how long it takes to draw a frame. - */ - void end_redraw() { - redraw_period_.post(nanos_now() - redraw_begin_time_); - } + /*! + Sets the frame rate for the target display. + */ + void set_frame_rate(float rate) { + frame_duration_ = Nanos(1'000'000'000.0f / rate); + } - /*! - Informs the predictor that a block-on-vsync has just ended, i.e. that the moment this - machine calls retrace is now. The predictor uses these notifications to estimate output - frame rate. - */ - void announce_vsync() { - const auto now = nanos_now(); + /*! + @returns The time this class currently believes a whole frame occupies. + */ + Time::Nanos frame_duration() { + return frame_duration_; + } - if(last_vsync_) { - last_vsync_ += frame_duration_; - vsync_jitter_.post(last_vsync_ - now); - last_vsync_ = (last_vsync_ + now) >> 1; - } else { - last_vsync_ = now; + /*! + Adds a record of how much jitter was experienced in scheduling; these values will be + factored into the @c suggested_draw_time if supplied. + + A positive number means the timer occurred late. A negative number means it occurred early. + */ + void add_timer_jitter(Time::Nanos jitter) { + timer_jitter_.post(jitter); + } + + /*! + Announces to the vsync predictor that output is now paused. This ends frame period + calculations until the next announce_vsync() restarts frame-length counting. + */ + void pause() { + last_vsync_ = 0; + } + + /*! + @return The time at which redrawing should begin, given the predicted frame period, how + long it appears to take to draw a frame and how much jitter there is in scheduling + (if those figures are being supplied). + */ + Nanos suggested_draw_time() { + const auto mean = redraw_period_.mean() + timer_jitter_.mean() + vsync_jitter_.mean(); + const auto variance = redraw_period_.variance() + timer_jitter_.variance() + vsync_jitter_.variance(); + + // Permit three standard deviations from the mean, to cover 99.9% of cases. + const auto period = mean + Nanos(3.0f * sqrt(float(variance))); + + return last_vsync_ + frame_duration_ - period; + } + +private: + class VarianceCollector { + public: + VarianceCollector(Time::Nanos default_value) { + sum_ = default_value * 128; + for(int c = 0; c < 128; ++c) { + history_[c] = default_value; + } } - } - /*! - Sets the frame rate for the target display. - */ - void set_frame_rate(float rate) { - frame_duration_ = Nanos(1'000'000'000.0f / rate); - } + void post(Time::Nanos value) { + sum_ -= history_[write_pointer_]; + sum_ += value; + history_[write_pointer_] = value; + write_pointer_ = (write_pointer_ + 1) & 127; + } - /*! - @returns The time this class currently believes a whole frame occupies. - */ - Time::Nanos frame_duration() { - return frame_duration_; - } + Time::Nanos mean() { + return sum_ / 128; + } - /*! - Adds a record of how much jitter was experienced in scheduling; these values will be - factored into the @c suggested_draw_time if supplied. - - A positive number means the timer occurred late. A negative number means it occurred early. - */ - void add_timer_jitter(Time::Nanos jitter) { - timer_jitter_.post(jitter); - } - - /*! - Announces to the vsync predictor that output is now paused. This ends frame period - calculations until the next announce_vsync() restarts frame-length counting. - */ - void pause() { - last_vsync_ = 0; - } - - /*! - @return The time at which redrawing should begin, given the predicted frame period, how - long it appears to take to draw a frame and how much jitter there is in scheduling - (if those figures are being supplied). - */ - Nanos suggested_draw_time() { - const auto mean = redraw_period_.mean() + timer_jitter_.mean() + vsync_jitter_.mean(); - const auto variance = redraw_period_.variance() + timer_jitter_.variance() + vsync_jitter_.variance(); - - // Permit three standard deviations from the mean, to cover 99.9% of cases. - const auto period = mean + Nanos(3.0f * sqrt(float(variance))); - - return last_vsync_ + frame_duration_ - period; - } - - private: - class VarianceCollector { - public: - VarianceCollector(Time::Nanos default_value) { - sum_ = default_value * 128; - for(int c = 0; c < 128; ++c) { - history_[c] = default_value; - } + Time::Nanos variance() { + // I haven't yet come up with a better solution that calculating this + // in whole every time, given the way that the mean mutates. + Time::Nanos variance = 0; + for(int c = 0; c < 128; ++c) { + const auto difference = ((history_[c] * 128) - sum_) / 128; + variance += (difference * difference); } + return variance / 128; + } - void post(Time::Nanos value) { - sum_ -= history_[write_pointer_]; - sum_ += value; - history_[write_pointer_] = value; - write_pointer_ = (write_pointer_ + 1) & 127; - } + private: + Time::Nanos sum_; + Time::Nanos history_[128]; + size_t write_pointer_ = 0; + }; - Time::Nanos mean() { - return sum_ / 128; - } + Nanos redraw_begin_time_ = 0; + Nanos last_vsync_ = 0; + Nanos frame_duration_ = 1'000'000'000 / 60; - Time::Nanos variance() { - // I haven't yet come up with a better solution that calculating this - // in whole every time, given the way that the mean mutates. - Time::Nanos variance = 0; - for(int c = 0; c < 128; ++c) { - const auto difference = ((history_[c] * 128) - sum_) / 128; - variance += (difference * difference); - } - return variance / 128; - } - - private: - Time::Nanos sum_; - Time::Nanos history_[128]; - size_t write_pointer_ = 0; - }; - - Nanos redraw_begin_time_ = 0; - Nanos last_vsync_ = 0; - Nanos frame_duration_ = 1'000'000'000 / 60; - - VarianceCollector vsync_jitter_{0}; - VarianceCollector redraw_period_{1'000'000'000 / 60}; // A less convincing first guess. - VarianceCollector timer_jitter_{0}; // Seed at 0 in case this feature isn't used by the owner. + VarianceCollector vsync_jitter_{0}; + VarianceCollector redraw_period_{1'000'000'000 / 60}; // A less convincing first guess. + VarianceCollector timer_jitter_{0}; // Seed at 0 in case this feature isn't used by the owner. }; } diff --git a/Components/1770/1770.cpp b/Components/1770/1770.cpp index cce894a12..cf11cb5b7 100644 --- a/Components/1770/1770.cpp +++ b/Components/1770/1770.cpp @@ -17,7 +17,7 @@ Log::Logger logger; using namespace WD; -WD1770::WD1770(Personality p) : +WD1770::WD1770(const Personality p) : Storage::Disk::MFMController(8000000), personality_(p), interesting_event_mask_(int(Event1770::Command)) { @@ -25,7 +25,7 @@ WD1770::WD1770(Personality p) : posit_event(int(Event1770::Command)); } -void WD1770::write(int address, uint8_t value) { +void WD1770::write(const int address, const uint8_t value) { switch(address&3) { case 0: { if((value&0xf0) == 0xd0) { @@ -56,7 +56,7 @@ void WD1770::write(int address, uint8_t value) { } } -uint8_t WD1770::read(int address) { +uint8_t WD1770::read(const int address) { switch(address&3) { default: { update_status([] (Status &status) { @@ -177,7 +177,7 @@ void WD1770::run_for(const Cycles cycles) { // ! 4 ! Forc int ! 1 1 0 1 i3 i2 i1 i0 ! // +--------+----------+-------------------------+ -void WD1770::posit_event(int new_event_type) { +void WD1770::posit_event(const int new_event_type) { if(new_event_type == int(Event::IndexHole)) { index_hole_count_++; if(index_hole_count_target_ == index_hole_count_) { @@ -809,7 +809,7 @@ void WD1770::posit_event(int new_event_type) { END_SECTION() } -void WD1770::update_status(std::function updater) { +void WD1770::update_status(const std::function updater) { const Status old_status = status_; if(delegate_) { @@ -827,7 +827,7 @@ void WD1770::update_status(std::function updater) { void WD1770::set_head_load_request(bool) {} void WD1770::set_motor_on(bool) {} -void WD1770::set_head_loaded(bool head_loaded) { +void WD1770::set_head_loaded(const bool head_loaded) { head_is_loaded_ = head_loaded; if(head_loaded) posit_event(int(Event1770::HeadLoad)); } diff --git a/Components/1770/1770.hpp b/Components/1770/1770.hpp index 23f9a2a82..fc8f05af7 100644 --- a/Components/1770/1770.hpp +++ b/Components/1770/1770.hpp @@ -17,126 +17,125 @@ namespace WD { WD1770, WD1772, FDC1773 and FDC1793. */ class WD1770: public Storage::Disk::MFMController { - public: - enum Personality { - P1770, // implies automatic motor-on management, with Type 2 commands offering a spin-up disable - P1772, // as per the 1770, with different stepping rates - P1773, // implements the side number-testing logic of the 1793; omits spin-up/loading logic - P1793 // implies Type 2 commands use side number testing logic; spin-up/loading is by HLD and HLT - }; +public: + enum Personality { + P1770, // implies automatic motor-on management, with Type 2 commands offering a spin-up disable + P1772, // as per the 1770, with different stepping rates + P1773, // implements the side number-testing logic of the 1793; omits spin-up/loading logic + P1793 // implies Type 2 commands use side number testing logic; spin-up/loading is by HLD and HLT + }; - /*! - Constructs an instance of the drive controller that behaves according to personality @c p. - @param p The type of controller to emulate. - */ - WD1770(Personality p); - virtual ~WD1770() = default; + /*! + Constructs an instance of the drive controller that behaves according to the specified personality. + */ + WD1770(Personality); + virtual ~WD1770() = default; - /// Sets the value of the double-density input; when @c is_double_density is @c true, reads and writes double-density format data. - using Storage::Disk::MFMController::set_is_double_density; + /// Sets the value of the double-density input; when @c is_double_density is @c true, reads and writes double-density format data. + using Storage::Disk::MFMController::set_is_double_density; - /// Writes @c value to the register at @c address. Only the low two bits of the address are decoded. - void write(int address, uint8_t value); + /// Writes @c value to the register at @c address. Only the low two bits of the address are decoded. + void write(int address, uint8_t value); - /// Fetches the value of the register @c address. Only the low two bits of the address are decoded. - uint8_t read(int address); + /// Fetches the value of the register @c address. Only the low two bits of the address are decoded. + uint8_t read(int address); - /// Runs the controller for @c number_of_cycles cycles. - void run_for(const Cycles cycles); + /// Runs the controller for @c number_of_cycles cycles. + void run_for(const Cycles cycles); - enum Flag: uint8_t { - NotReady = 0x80, // 0x80 - MotorOn = 0x80, - WriteProtect = 0x40, // 0x40 - RecordType = 0x20, // 0x20 - SpinUp = 0x20, - HeadLoaded = 0x20, - RecordNotFound = 0x10, // 0x10 - SeekError = 0x10, - CRCError = 0x08, // 0x08 - LostData = 0x04, // 0x04 - TrackZero = 0x04, - DataRequest = 0x02, // 0x02 - Index = 0x02, - Busy = 0x01 // 0x01 - }; + enum Flag: uint8_t { + NotReady = 0x80, // 0x80 + MotorOn = 0x80, + WriteProtect = 0x40, // 0x40 + RecordType = 0x20, // 0x20 + SpinUp = 0x20, + HeadLoaded = 0x20, + RecordNotFound = 0x10, // 0x10 + SeekError = 0x10, + CRCError = 0x08, // 0x08 + LostData = 0x04, // 0x04 + TrackZero = 0x04, + DataRequest = 0x02, // 0x02 + Index = 0x02, + Busy = 0x01 // 0x01 + }; - /// @returns The current value of the IRQ line output. - inline bool get_interrupt_request_line() const { return status_.interrupt_request; } + /// @returns The current value of the IRQ line output. + inline bool get_interrupt_request_line() const { return status_.interrupt_request; } - /// @returns The current value of the DRQ line output. - inline bool get_data_request_line() const { return status_.data_request; } + /// @returns The current value of the DRQ line output. + inline bool get_data_request_line() const { return status_.data_request; } - class Delegate { - public: - virtual void wd1770_did_change_output(WD1770 *wd1770) = 0; - }; - inline void set_delegate(Delegate *delegate) { delegate_ = delegate; } + class Delegate { + public: + virtual void wd1770_did_change_output(WD1770 *wd1770) = 0; + }; + inline void set_delegate(Delegate *delegate) { delegate_ = delegate; } - ClockingHint::Preference preferred_clocking() const final; + ClockingHint::Preference preferred_clocking() const final; - protected: - virtual void set_head_load_request(bool head_load); - virtual void set_motor_on(bool motor_on); - void set_head_loaded(bool head_loaded); +protected: + virtual void set_head_load_request(bool head_load); + virtual void set_motor_on(bool motor_on); + void set_head_loaded(bool head_loaded); - /// @returns The last value posted to @c set_head_loaded. - bool get_head_loaded() const; + /// @returns The last value posted to @c set_head_loaded. + bool get_head_loaded() const; - private: - const Personality personality_; - bool has_motor_on_line() const { return (personality_ != P1793 ) && (personality_ != P1773); } - bool has_head_load_line() const { return (personality_ == P1793 ); } +private: + const Personality personality_; + bool has_motor_on_line() const { return (personality_ != P1793 ) && (personality_ != P1773); } + bool has_head_load_line() const { return (personality_ == P1793 ); } - struct Status { - bool write_protect = false; - bool record_type = false; - bool spin_up = false; - bool record_not_found = false; - bool crc_error = false; - bool seek_error = false; - bool lost_data = false; - bool data_request = false; - bool interrupt_request = false; - bool busy = false; - bool track_zero = false; - enum { - One, Two, Three - } type = One; - } status_; - uint8_t track_; - uint8_t sector_; - uint8_t data_; - uint8_t command_; + struct Status { + bool write_protect = false; + bool record_type = false; + bool spin_up = false; + bool record_not_found = false; + bool crc_error = false; + bool seek_error = false; + bool lost_data = false; + bool data_request = false; + bool interrupt_request = false; + bool busy = false; + bool track_zero = false; + enum { + One, Two, Three + } type = One; + } status_; + uint8_t track_; + uint8_t sector_; + uint8_t data_; + uint8_t command_; - int index_hole_count_; - int index_hole_count_target_ = -1; - int distance_into_section_; + int index_hole_count_; + int index_hole_count_target_ = -1; + int distance_into_section_; - int step_direction_; - void update_status(std::function updater); + int step_direction_; + void update_status(std::function updater); - // Events - enum Event1770: int { - Command = (1 << 3), // Indicates receipt of a new command. - HeadLoad = (1 << 4), // Indicates the head has been loaded (1973 only). - Timer = (1 << 5), // Indicates that the delay_time_-powered timer has timed out. - IndexHoleTarget = (1 << 6), // Indicates that index_hole_count_ has reached index_hole_count_target_. - ForceInterrupt = (1 << 7) // Indicates a forced interrupt. - }; - void posit_event(int type); - int interesting_event_mask_; - int resume_point_ = 0; - Cycles::IntType delay_time_ = 0; + // Events + enum Event1770: int { + Command = (1 << 3), // Indicates receipt of a new command. + HeadLoad = (1 << 4), // Indicates the head has been loaded (1973 only). + Timer = (1 << 5), // Indicates that the delay_time_-powered timer has timed out. + IndexHoleTarget = (1 << 6), // Indicates that index_hole_count_ has reached index_hole_count_target_. + ForceInterrupt = (1 << 7) // Indicates a forced interrupt. + }; + void posit_event(int type); + int interesting_event_mask_; + int resume_point_ = 0; + Cycles::IntType delay_time_ = 0; - // ID buffer - uint8_t header_[6]; + // ID buffer + uint8_t header_[6]; - // 1793 head-loading logic - bool head_is_loaded_ = false; + // 1793 head-loading logic + bool head_is_loaded_ = false; - // delegate - Delegate *delegate_ = nullptr; + // delegate + Delegate *delegate_ = nullptr; }; } diff --git a/Components/5380/ncr5380.cpp b/Components/5380/ncr5380.cpp index 564776cde..d58068c62 100644 --- a/Components/5380/ncr5380.cpp +++ b/Components/5380/ncr5380.cpp @@ -21,7 +21,7 @@ Log::Logger logger; using namespace NCR::NCR5380; using SCSI::Line; -NCR5380::NCR5380(SCSI::Bus &bus, int clock_rate) : +NCR5380::NCR5380(SCSI::Bus &bus, const int clock_rate) : bus_(bus), clock_rate_(clock_rate) { device_id_ = bus_.add_device(); @@ -33,7 +33,7 @@ NCR5380::NCR5380(SCSI::Bus &bus, int clock_rate) : (void)expected_phase_; } -void NCR5380::write(int address, uint8_t value, bool) { +void NCR5380::write(const int address, const uint8_t value, bool) { switch(address & 7) { case 0: logger.info().append("[0] Set current SCSI bus state to %02x", value); @@ -101,11 +101,11 @@ void NCR5380::write(int address, uint8_t value, bool) { update_control_output(); break; - case 3: { + case 3: logger.info().append("[3] Set target command: %02x", value); target_command_ = value; update_control_output(); - } break; + break; case 4: logger.info().append("[4] Set select enabled: %02x", value); @@ -143,7 +143,7 @@ void NCR5380::write(int address, uint8_t value, bool) { } } -uint8_t NCR5380::read(int address, bool) { +uint8_t NCR5380::read(const int address, bool) { switch(address & 7) { case 0: logger.info().append("[0] Get current SCSI bus state: %02x", (bus_.get_state() & 0xff)); @@ -154,7 +154,10 @@ uint8_t NCR5380::read(int address, bool) { return uint8_t(bus_.get_state()); case 1: - logger.info().append("[1] Initiator command register get: %c%c", arbitration_in_progress_ ? 'p' : '-', lost_arbitration_ ? 'l' : '-'); + logger.info().append( + "[1] Initiator command register get: %c%c", + arbitration_in_progress_ ? 'p' : '-', + lost_arbitration_ ? 'l' : '-'); return // Bits repeated as they were set. (initiator_command_ & ~0x60) | @@ -239,7 +242,7 @@ void NCR5380::update_control_output() { } } -void NCR5380::scsi_bus_did_change(SCSI::Bus *, SCSI::BusState new_state, double time_since_change) { +void NCR5380::scsi_bus_did_change(SCSI::Bus *, const SCSI::BusState new_state, const double time_since_change) { /* When connected as an Initiator with DMA Mode True, if the phase lines I//O, C//D, and /MSG do not match the @@ -333,7 +336,7 @@ void NCR5380::scsi_bus_did_change(SCSI::Bus *, SCSI::BusState new_state, double } } -void NCR5380::set_execution_state(ExecutionState state) { +void NCR5380::set_execution_state(const ExecutionState state) { state_ = state; if(state != ExecutionState::PerformingDMA) dma_operation_ = DMAOperation::Ready; } @@ -357,7 +360,7 @@ uint8_t NCR5380::dma_acknowledge() { return bus_state; } -void NCR5380::dma_acknowledge(uint8_t value) { +void NCR5380::dma_acknowledge(const uint8_t value) { data_bus_ = value; dma_acknowledge_ = true; diff --git a/Components/5380/ncr5380.hpp b/Components/5380/ncr5380.hpp index 48a7d3e0a..38120795b 100644 --- a/Components/5380/ncr5380.hpp +++ b/Components/5380/ncr5380.hpp @@ -19,69 +19,69 @@ namespace NCR::NCR5380 { Models the NCR 5380, a SCSI interface chip. */ class NCR5380 final: public SCSI::Bus::Observer { - public: - NCR5380(SCSI::Bus &bus, int clock_rate); +public: + NCR5380(SCSI::Bus &, int clock_rate); - /*! Writes @c value to @c address. */ - void write(int address, uint8_t value, bool dma_acknowledge = false); + /*! Writes @c value to @c address. */ + void write(int address, uint8_t value, bool dma_acknowledge = false); - /*! Reads from @c address. */ - uint8_t read(int address, bool dma_acknowledge = false); + /*! Reads from @c address. */ + uint8_t read(int address, bool dma_acknowledge = false); - /*! @returns The SCSI ID assigned to this device. */ - size_t scsi_id(); + /*! @returns The SCSI ID assigned to this device. */ + size_t scsi_id(); - /*! @return @c true if DMA request is active; @c false otherwise. */ - bool dma_request(); + /*! @return @c true if DMA request is active; @c false otherwise. */ + bool dma_request(); - /*! Signals DMA acknowledge with a simultaneous read. */ - uint8_t dma_acknowledge(); + /*! Signals DMA acknowledge with a simultaneous read. */ + uint8_t dma_acknowledge(); - /*! Signals DMA acknowledge with a simultaneous write. */ - void dma_acknowledge(uint8_t); + /*! Signals DMA acknowledge with a simultaneous write. */ + void dma_acknowledge(uint8_t); - private: - SCSI::Bus &bus_; +private: + SCSI::Bus &bus_; - const int clock_rate_; - size_t device_id_; + const int clock_rate_; + size_t device_id_; - SCSI::BusState bus_output_ = SCSI::DefaultBusState; - SCSI::BusState expected_phase_ = SCSI::DefaultBusState; - uint8_t mode_ = 0xff; - uint8_t initiator_command_ = 0xff; - uint8_t data_bus_ = 0xff; - uint8_t target_command_ = 0xff; - bool test_mode_ = false; - bool assert_data_bus_ = false; - bool dma_request_ = false; - bool dma_acknowledge_ = false; - bool end_of_dma_ = false; + SCSI::BusState bus_output_ = SCSI::DefaultBusState; + SCSI::BusState expected_phase_ = SCSI::DefaultBusState; + uint8_t mode_ = 0xff; + uint8_t initiator_command_ = 0xff; + uint8_t data_bus_ = 0xff; + uint8_t target_command_ = 0xff; + bool test_mode_ = false; + bool assert_data_bus_ = false; + bool dma_request_ = false; + bool dma_acknowledge_ = false; + bool end_of_dma_ = false; - bool irq_ = false; - bool phase_mismatch_ = false; + bool irq_ = false; + bool phase_mismatch_ = false; - enum class ExecutionState { - None, - WaitingForBusy, - WatchingBusy, - PerformingDMA, - } state_ = ExecutionState::None; - enum class DMAOperation { - Ready, - Send, - TargetReceive, - InitiatorReceive - } dma_operation_ = DMAOperation::Ready; - bool lost_arbitration_ = false, arbitration_in_progress_ = false; + enum class ExecutionState { + None, + WaitingForBusy, + WatchingBusy, + PerformingDMA, + } state_ = ExecutionState::None; + enum class DMAOperation { + Ready, + Send, + TargetReceive, + InitiatorReceive + } dma_operation_ = DMAOperation::Ready; + bool lost_arbitration_ = false, arbitration_in_progress_ = false; - void set_execution_state(ExecutionState state); + void set_execution_state(ExecutionState state); - SCSI::BusState target_output() const; - void update_control_output(); + SCSI::BusState target_output() const; + void update_control_output(); - void scsi_bus_did_change(SCSI::Bus *, SCSI::BusState new_state, double time_since_change) final; - bool phase_matches() const; + void scsi_bus_did_change(SCSI::Bus *, SCSI::BusState new_state, double time_since_change) final; + bool phase_matches() const; }; } diff --git a/Components/6522/6522.hpp b/Components/6522/6522.hpp index 733fccd75..9cf63d746 100644 --- a/Components/6522/6522.hpp +++ b/Components/6522/6522.hpp @@ -31,26 +31,26 @@ enum Line { 6522 and a subclass of PortHandler in order to reproduce a 6522 and its original bus wiring. */ class PortHandler { - public: - /// Requests the current input value of @c port from the port handler. - uint8_t get_port_input([[maybe_unused]] Port port) { - return 0xff; - } +public: + /// Requests the current input value of the named port from the port handler. + uint8_t get_port_input(Port) { + return 0xff; + } - /// Sets the current output value of @c port and provides @c direction_mask, indicating which pins are marked as output. - void set_port_output([[maybe_unused]] Port port, [[maybe_unused]] uint8_t value, [[maybe_unused]] uint8_t direction_mask) {} + /// Sets the current output value of the named oprt and provides @c direction_mask, indicating which pins are marked as output. + void set_port_output(Port, [[maybe_unused]] uint8_t value, [[maybe_unused]] uint8_t direction_mask) {} - /// Sets the current logical output level for line @c line on port @c port. - void set_control_line_output([[maybe_unused]] Port port, [[maybe_unused]] Line line, [[maybe_unused]] bool value) {} + /// Sets the current logical output level for the named line on the specified port. + void set_control_line_output(Port, Line, [[maybe_unused]] bool value) {} - /// Sets the current logical value of the interrupt line. - void set_interrupt_status([[maybe_unused]] bool status) {} + /// Sets the current logical value of the interrupt line. + void set_interrupt_status([[maybe_unused]] bool status) {} - /// Provides a measure of time elapsed between other calls. - void run_for([[maybe_unused]] HalfCycles duration) {} + /// Provides a measure of time elapsed between other calls. + void run_for(HalfCycles) {} - /// Receives passed-on flush() calls from the 6522. - void flush() {} + /// Receives passed-on flush() calls from the 6522. + void flush() {} }; /*! @@ -58,21 +58,21 @@ class PortHandler { a virtual level of indirection for receiving changes to the interrupt line. */ class IRQDelegatePortHandler: public PortHandler { - public: - class Delegate { - public: - /// Indicates that the interrupt status has changed for the IRQDelegatePortHandler provided. - virtual void mos6522_did_change_interrupt_status(void *irq_delegate) = 0; - }; +public: + class Delegate { + public: + /// Indicates that the interrupt status has changed for the IRQDelegatePortHandler provided. + virtual void mos6522_did_change_interrupt_status(void *irq_delegate) = 0; + }; - /// Sets the delegate that will receive notification of changes in the interrupt line. - void set_interrupt_delegate(Delegate *delegate); + /// Sets the delegate that will receive notification of changes in the interrupt line. + void set_interrupt_delegate(Delegate *); - /// Overrides @c PortHandler::set_interrupt_status, notifying the delegate if one is set. - void set_interrupt_status(bool new_status); + /// Overrides @c PortHandler::set_interrupt_status, notifying the delegate if one is set. + void set_interrupt_status(bool); - private: - Delegate *delegate_ = nullptr; +private: + Delegate *delegate_ = nullptr; }; /*! @@ -87,53 +87,53 @@ class IRQDelegatePortHandler: public PortHandler { implementing bus communications as required. */ template class MOS6522: public MOS6522Storage { - public: - MOS6522(BusHandlerT &bus_handler) noexcept : bus_handler_(bus_handler) {} - MOS6522(const MOS6522 &) = delete; +public: + MOS6522(BusHandlerT &bus_handler) noexcept : bus_handler_(bus_handler) {} + MOS6522(const MOS6522 &) = delete; - /*! Sets a register value. */ - void write(int address, uint8_t value); + /*! Sets a register value. */ + void write(int address, uint8_t value); - /*! Gets a register value. */ - uint8_t read(int address); + /*! Gets a register value. */ + uint8_t read(int address); - /*! @returns the bus handler. */ - BusHandlerT &bus_handler(); + /*! @returns the bus handler. */ + BusHandlerT &bus_handler(); - /// Sets the input value of line @c line on port @c port. - void set_control_line_input(Port port, Line line, bool value); + /// Sets the input value of the named line and port. + void set_control_line_input(Port, Line, bool value); - /// Runs for a specified number of half cycles. - void run_for(const HalfCycles half_cycles); + /// Runs for a specified number of half cycles. + void run_for(const HalfCycles); - /// Runs for a specified number of cycles. - void run_for(const Cycles cycles); + /// Runs for a specified number of cycles. + void run_for(const Cycles); - /// @returns @c true if the IRQ line is currently active; @c false otherwise. - bool get_interrupt_line() const; + /// @returns @c true if the IRQ line is currently active; @c false otherwise. + bool get_interrupt_line() const; - /// Updates the port handler to the current time and then requests that it flush. - void flush(); + /// Updates the port handler to the current time and then requests that it flush. + void flush(); - private: - void do_phase1(); - void do_phase2(); - void shift_in(); - void shift_out(); +private: + void do_phase1(); + void do_phase2(); + void shift_in(); + void shift_out(); - BusHandlerT &bus_handler_; - HalfCycles time_since_bus_handler_call_; + BusHandlerT &bus_handler_; + HalfCycles time_since_bus_handler_call_; - void access(int address); + void access(int address); - uint8_t get_port_input(Port port, uint8_t output_mask, uint8_t output, uint8_t timer_mask); - inline void reevaluate_interrupts(); + uint8_t get_port_input(Port port, uint8_t output_mask, uint8_t output, uint8_t timer_mask); + inline void reevaluate_interrupts(); - /// Sets the current intended output value for the port and line; - /// if this affects the visible output, it will be passed to the handler. - void set_control_line_output(Port port, Line line, LineState value); - void evaluate_cb2_output(); - void evaluate_port_b_output(); + /// Sets the current intended output value for the port and line; + /// if this affects the visible output, it will be passed to the handler. + void set_control_line_output(Port port, Line line, LineState value); + void evaluate_cb2_output(); + void evaluate_port_b_output(); }; } diff --git a/Components/6522/Implementation/6522Implementation.hpp b/Components/6522/Implementation/6522Implementation.hpp index 29c4aed12..a4ddfd4df 100644 --- a/Components/6522/Implementation/6522Implementation.hpp +++ b/Components/6522/Implementation/6522Implementation.hpp @@ -14,7 +14,7 @@ namespace MOS::MOS6522 { -template void MOS6522::access(int address) { +template void MOS6522::access(const int address) { switch(address) { case 0x0: // In both handshake and pulse modes, CB2 goes low on any read or write of Port B. @@ -33,7 +33,7 @@ template void MOS6522::access(int address) { } } -template void MOS6522::write(int address, uint8_t value) { +template void MOS6522::write(int address, const uint8_t value) { address &= 0xf; access(address); switch(address) { @@ -44,7 +44,10 @@ template void MOS6522::write(int address, uint8_t value) { bus_handler_.run_for(time_since_bus_handler_call_.flush()); evaluate_port_b_output(); - registers_.interrupt_flags &= ~(InterruptFlag::CB1ActiveEdge | ((registers_.peripheral_control&0x20) ? 0 : InterruptFlag::CB2ActiveEdge)); + registers_.interrupt_flags &= ~( + InterruptFlag::CB1ActiveEdge | + ((registers_.peripheral_control&0x20) ? 0 : InterruptFlag::CB2ActiveEdge) + ); reevaluate_interrupts(); break; case 0xf: @@ -58,7 +61,10 @@ template void MOS6522::write(int address, uint8_t value) { set_control_line_output(Port::A, Line::Two, LineState::Off); } - registers_.interrupt_flags &= ~(InterruptFlag::CA1ActiveEdge | ((registers_.peripheral_control&0x02) ? 0 : InterruptFlag::CB2ActiveEdge)); + registers_.interrupt_flags &= ~( + InterruptFlag::CA1ActiveEdge | + ((registers_.peripheral_control&0x02) ? 0 : InterruptFlag::CB2ActiveEdge) + ); reevaluate_interrupts(); break; @@ -230,7 +236,12 @@ template uint8_t MOS6522::read(int address) { return 0xff; } -template uint8_t MOS6522::get_port_input(Port port, uint8_t output_mask, uint8_t output, uint8_t timer_mask) { +template uint8_t MOS6522::get_port_input( + const Port port, + const uint8_t output_mask, + uint8_t output, + const uint8_t timer_mask +) { bus_handler_.run_for(time_since_bus_handler_call_.flush()); const uint8_t input = bus_handler_.get_port_input(port); output = (output & ~timer_mask) | (registers_.timer_port_b_output & timer_mask); @@ -252,7 +263,7 @@ template void MOS6522::reevaluate_interrupts() { } } -template void MOS6522::set_control_line_input(Port port, Line line, bool value) { +template void MOS6522::set_control_line_input(const Port port, const Line line, const bool value) { switch(line) { case Line::One: if(value != control_inputs_[port].lines[line]) { @@ -448,7 +459,8 @@ template void MOS6522::evaluate_cb2_output() { } } -template void MOS6522::set_control_line_output(Port port, Line line, LineState value) { +template +void MOS6522::set_control_line_output(const Port port, const Line line, const LineState value) { if(port == Port::B && line == Line::Two) { control_outputs_[port].lines[line] = value; evaluate_cb2_output(); diff --git a/Components/6522/Implementation/6522Storage.hpp b/Components/6522/Implementation/6522Storage.hpp index 330081e9a..a143a1370 100644 --- a/Components/6522/Implementation/6522Storage.hpp +++ b/Components/6522/Implementation/6522Storage.hpp @@ -13,96 +13,96 @@ namespace MOS::MOS6522 { class MOS6522Storage { - protected: - // Phase toggle - bool is_phase2_ = false; +protected: + // Phase toggle + bool is_phase2_ = false; - // The registers - struct Registers { - // "A low reset (RES) input clears all R6522 internal registers to logic 0" - uint8_t output[2] = {0, 0}; - uint8_t input[2] = {0, 0}; - uint8_t data_direction[2] = {0, 0}; - uint16_t timer[2] = {0, 0}; - uint16_t timer_latch[2] = {0, 0}; - uint16_t last_timer[2] = {0, 0}; - int next_timer[2] = {-1, -1}; - uint8_t shift = 0; - uint8_t auxiliary_control = 0; - uint8_t peripheral_control = 0; - uint8_t interrupt_flags = 0; - uint8_t interrupt_enable = 0; + // The registers + struct Registers { + // "A low reset (RES) input clears all R6522 internal registers to logic 0" + uint8_t output[2] = {0, 0}; + uint8_t input[2] = {0, 0}; + uint8_t data_direction[2] = {0, 0}; + uint16_t timer[2] = {0, 0}; + uint16_t timer_latch[2] = {0, 0}; + uint16_t last_timer[2] = {0, 0}; + int next_timer[2] = {-1, -1}; + uint8_t shift = 0; + uint8_t auxiliary_control = 0; + uint8_t peripheral_control = 0; + uint8_t interrupt_flags = 0; + uint8_t interrupt_enable = 0; - bool timer_needs_reload = false; - uint8_t timer_port_b_output = 0xff; - } registers_; + bool timer_needs_reload = false; + uint8_t timer_port_b_output = 0xff; + } registers_; - // Control state. - struct { - bool lines[2] = {false, false}; - } control_inputs_[2]; + // Control state. + struct { + bool lines[2] = {false, false}; + } control_inputs_[2]; - enum class LineState { - On, Off, Input - }; - struct { - LineState lines[2] = {LineState::Input, LineState::Input}; - } control_outputs_[2]; + enum class LineState { + On, Off, Input + }; + struct { + LineState lines[2] = {LineState::Input, LineState::Input}; + } control_outputs_[2]; - enum class HandshakeMode { - None, - Handshake, - Pulse - } handshake_modes_[2] = { HandshakeMode::None, HandshakeMode::None }; + enum class HandshakeMode { + None, + Handshake, + Pulse + } handshake_modes_[2] = { HandshakeMode::None, HandshakeMode::None }; - bool timer_is_running_[2] = {false, false}; - bool last_posted_interrupt_status_ = false; - int shift_bits_remaining_ = 8; + bool timer_is_running_[2] = {false, false}; + bool last_posted_interrupt_status_ = false; + int shift_bits_remaining_ = 8; - enum InterruptFlag: uint8_t { - CA2ActiveEdge = 1 << 0, - CA1ActiveEdge = 1 << 1, - ShiftRegister = 1 << 2, - CB2ActiveEdge = 1 << 3, - CB1ActiveEdge = 1 << 4, - Timer2 = 1 << 5, - Timer1 = 1 << 6, - }; + enum InterruptFlag: uint8_t { + CA2ActiveEdge = 1 << 0, + CA1ActiveEdge = 1 << 1, + ShiftRegister = 1 << 2, + CB2ActiveEdge = 1 << 3, + CB1ActiveEdge = 1 << 4, + Timer2 = 1 << 5, + Timer1 = 1 << 6, + }; - enum class ShiftMode { - Disabled = 0, - InUnderT2 = 1, - InUnderPhase2 = 2, - InUnderCB1 = 3, - OutUnderT2FreeRunning = 4, - OutUnderT2 = 5, - OutUnderPhase2 = 6, - OutUnderCB1 = 7 - }; - bool timer1_is_controlling_pb7() const { - return registers_.auxiliary_control & 0x80; - } - bool timer1_is_continuous() const { - return registers_.auxiliary_control & 0x40; - } - bool is_shifting_out() const { - return registers_.auxiliary_control & 0x10; - } - int timer2_clock_decrement() const { - return 1 ^ ((registers_.auxiliary_control >> 5)&1); - } - int timer2_pb6_decrement() const { - return (registers_.auxiliary_control >> 5)&1; - } - ShiftMode shift_mode() const { - return ShiftMode((registers_.auxiliary_control >> 2) & 7); - } - bool portb_is_latched() const { - return registers_.auxiliary_control & 0x02; - } - bool port1_is_latched() const { - return registers_.auxiliary_control & 0x01; - } + enum class ShiftMode { + Disabled = 0, + InUnderT2 = 1, + InUnderPhase2 = 2, + InUnderCB1 = 3, + OutUnderT2FreeRunning = 4, + OutUnderT2 = 5, + OutUnderPhase2 = 6, + OutUnderCB1 = 7 + }; + bool timer1_is_controlling_pb7() const { + return registers_.auxiliary_control & 0x80; + } + bool timer1_is_continuous() const { + return registers_.auxiliary_control & 0x40; + } + bool is_shifting_out() const { + return registers_.auxiliary_control & 0x10; + } + int timer2_clock_decrement() const { + return 1 ^ ((registers_.auxiliary_control >> 5)&1); + } + int timer2_pb6_decrement() const { + return (registers_.auxiliary_control >> 5)&1; + } + ShiftMode shift_mode() const { + return ShiftMode((registers_.auxiliary_control >> 2) & 7); + } + bool portb_is_latched() const { + return registers_.auxiliary_control & 0x02; + } + bool port1_is_latched() const { + return registers_.auxiliary_control & 0x01; + } }; } diff --git a/Components/6522/Implementation/IRQDelegatePortHandler.cpp b/Components/6522/Implementation/IRQDelegatePortHandler.cpp index 54e80c75b..c2648afdd 100644 --- a/Components/6522/Implementation/IRQDelegatePortHandler.cpp +++ b/Components/6522/Implementation/IRQDelegatePortHandler.cpp @@ -10,7 +10,7 @@ using namespace MOS::MOS6522; -void IRQDelegatePortHandler::set_interrupt_delegate(Delegate *delegate) { +void IRQDelegatePortHandler::set_interrupt_delegate(Delegate *const delegate) { delegate_ = delegate; } diff --git a/Components/6526/6526.hpp b/Components/6526/6526.hpp index 751d91616..2ce79397c 100644 --- a/Components/6526/6526.hpp +++ b/Components/6526/6526.hpp @@ -41,49 +41,49 @@ template class MOS6526: private MOS6526Storage, private Serial::Line::ReadDelegate { - public: - MOS6526(PortHandlerT &port_handler) noexcept : port_handler_(port_handler) { - serial_input.set_read_delegate(this); - } - MOS6526(const MOS6526 &) = delete; +public: + MOS6526(PortHandlerT &port_handler) noexcept : port_handler_(port_handler) { + serial_input.set_read_delegate(this); + } + MOS6526(const MOS6526 &) = delete; - /// Writes @c value to the register at @c address. Only the low two bits of the address are decoded. - void write(int address, uint8_t value); + /// Writes @c value to the register at @c address. Only the low two bits of the address are decoded. + void write(int address, uint8_t value); - /// Fetches the value of the register @c address. Only the low two bits of the address are decoded. - uint8_t read(int address); + /// Fetches the value of the register @c address. Only the low two bits of the address are decoded. + uint8_t read(int address); - /// Pulses Phi2 to advance by the specified number of half cycles. - void run_for(const HalfCycles half_cycles); + /// Pulses Phi2 to advance by the specified number of half cycles. + void run_for(const HalfCycles); - /// Pulses the TOD input the specified number of times. - void advance_tod(int count); + /// Pulses the TOD input the specified number of times. + void advance_tod(int count); - /// @returns @c true if the interrupt output is active, @c false otherwise. - bool get_interrupt_line(); + /// @returns @c true if the interrupt output is active, @c false otherwise. + bool get_interrupt_line(); - /// Sets the current state of the CNT input. - void set_cnt_input(bool active); + /// Sets the current state of the CNT input. + void set_cnt_input(bool); - /// Provides both the serial input bit and an additional source of CNT. - Serial::Line serial_input; + /// Provides both the serial input bit and an additional source of CNT. + Serial::Line serial_input; - /// Sets the current state of the FLG input. - void set_flag_input(bool low); + /// Sets the current state of the FLG input. + void set_flag_input(bool); - private: - PortHandlerT &port_handler_; - TODStorage tod_; +private: + PortHandlerT &port_handler_; + TODStorage tod_; - template void set_port_output(); - template uint8_t get_port_input(); - void update_interrupts(); - void posit_interrupt(uint8_t mask); - void advance_counters(int); + template void set_port_output(); + template uint8_t get_port_input(); + void update_interrupts(); + void posit_interrupt(uint8_t mask); + void advance_counters(int); - bool serial_line_did_produce_bit(Serial::Line *line, int bit) final; + bool serial_line_did_produce_bit(Serial::Line *line, int bit) final; - Log::Logger log; + Log::Logger log; }; } diff --git a/Components/6526/Implementation/6526Implementation.hpp b/Components/6526/Implementation/6526Implementation.hpp index 543e21195..1c3148cda 100644 --- a/Components/6526/Implementation/6526Implementation.hpp +++ b/Components/6526/Implementation/6526Implementation.hpp @@ -33,7 +33,7 @@ template uint8_t MOS6526::get_port_input() } template -void MOS6526::posit_interrupt(uint8_t mask) { +void MOS6526::posit_interrupt(const uint8_t mask) { if(!mask) { return; } @@ -54,13 +54,13 @@ bool MOS6526::get_interrupt_line() { } template -void MOS6526::set_cnt_input(bool active) { +void MOS6526::set_cnt_input(const bool active) { cnt_edge_ = active && !cnt_state_; cnt_state_ = active; } template -void MOS6526::set_flag_input(bool low) { +void MOS6526::set_flag_input(const bool low) { if(low && !flag_state_) { posit_interrupt(Interrupts::Flag); } @@ -68,7 +68,7 @@ void MOS6526::set_flag_input(bool low) { } template -void MOS6526::write(int address, uint8_t value) { +void MOS6526::write(int address, const uint8_t value) { address &= 0xf; switch(address) { // Port output. @@ -204,7 +204,7 @@ void MOS6526::run_for(const HalfCycles half_cycles) { } template -void MOS6526::advance_tod(int count) { +void MOS6526::advance_tod(const int count) { if(!count) return; if(tod_.advance(count)) { posit_interrupt(Interrupts::Alarm); @@ -212,7 +212,7 @@ void MOS6526::advance_tod(int count) { } template -bool MOS6526::serial_line_did_produce_bit(Serial::Line *, int bit) { +bool MOS6526::serial_line_did_produce_bit(Serial::Line *, const int bit) { // TODO: post CNT change; might affect timer. if(!shifter_is_output_) { diff --git a/Components/6526/Implementation/6526Storage.hpp b/Components/6526/Implementation/6526Storage.hpp index 35db218aa..0d7c8b5be 100644 --- a/Components/6526/Implementation/6526Storage.hpp +++ b/Components/6526/Implementation/6526Storage.hpp @@ -16,161 +16,161 @@ namespace MOS::MOS6526 { class TODBase { - public: - template void set_control(uint8_t value) { - if constexpr (is_timer2) { - write_alarm = value & 0x80; - } else { - is_50Hz = value & 0x80; - } +public: + template void set_control(const uint8_t value) { + if constexpr (is_timer2) { + write_alarm = value & 0x80; + } else { + is_50Hz = value & 0x80; } + } - protected: - bool write_alarm = false, is_50Hz = false; +protected: + bool write_alarm = false, is_50Hz = false; }; template class TODStorage {}; template <> class TODStorage: public TODBase { - private: - bool increment_ = true, latched_ = false; - int divider_ = 0; - std::array value_; - std::array latch_; - std::array alarm_; +private: + bool increment_ = true, latched_ = false; + int divider_ = 0; + std::array value_; + std::array latch_; + std::array alarm_; - static constexpr uint8_t masks[4] = {0xf, 0x3f, 0x3f, 0x1f}; + static constexpr uint8_t masks[4] = {0xf, 0x3f, 0x3f, 0x1f}; - void bcd_increment(uint8_t &value) { - ++value; - if((value&0x0f) > 0x09) value += 0x06; - } + void bcd_increment(uint8_t &value) { + ++value; + if((value&0x0f) > 0x09) value += 0x06; + } - public: - template void write(uint8_t v) { - if(write_alarm) { - alarm_[byte] = v & masks[byte]; - } else { - value_[byte] = v & masks[byte]; - - if constexpr (byte == 0) { - increment_ = true; - } - if constexpr (byte == 3) { - increment_ = false; - } - } - } - - template uint8_t read() { - if(latched_) { - const uint8_t result = latch_[byte]; - if constexpr (byte == 0) { - latched_ = false; - } - return result; - } - - if constexpr (byte == 3) { - latched_ = true; - latch_ = value_; - } - return value_[byte]; - } - - bool advance(int count) { - if(!increment_) { - return false; - } - - while(count--) { - // Increment the pre-10ths divider. - ++divider_; - if(divider_ < 5) continue; - if(divider_ < 6 && !is_50Hz) continue; - divider_ = 0; - - // Increments 10ths of a second. One BCD digit. - ++value_[0]; - if(value_[0] < 10) { - continue; - } - - // Increment seconds. Actual BCD needed from here onwards. - bcd_increment(value_[1]); - if(value_[1] != 60) { - continue; - } - value_[1] = 0; - - // Increment minutes. - bcd_increment(value_[2]); - if(value_[2] != 60) { - continue; - } - value_[2] = 0; - - // TODO: increment hours, keeping AM/PM separate? - } - - return false; // TODO: test against alarm. - } -}; - -template <> class TODStorage: public TODBase { - private: - uint32_t increment_mask_ = uint32_t(~0); - uint32_t latch_ = 0; - uint32_t value_ = 0; - uint32_t alarm_ = 0xff'ffff; - - public: - template void write(uint8_t v) { - if constexpr (byte == 3) { - return; - } - constexpr int shift = byte << 3; - - // Write to either the alarm or the current value as directed; - // writing to any part of the current value other than the LSB - // pauses incrementing until the LSB is written. - const uint32_t mask = uint32_t(~(0xff << shift)); - if(write_alarm) { - alarm_ = (alarm_ & mask) | uint32_t(v << shift); - } else { - value_ = (value_ & mask) | uint32_t(v << shift); - increment_mask_ = (byte == 0) ? uint32_t(~0) : 0; - } - } - - template uint8_t read() { - if constexpr (byte == 3) { - return 0xff; // Assumed. Just a guess. - } - constexpr int shift = byte << 3; - - if constexpr (byte == 2) { - latch_ = value_ | 0xff00'0000; - } - - const uint32_t source = latch_ ? latch_ : value_; - const uint8_t result = uint8_t((source >> shift) & 0xff); +public: + template void write(const uint8_t v) { + if(write_alarm) { + alarm_[byte] = v & masks[byte]; + } else { + value_[byte] = v & masks[byte]; if constexpr (byte == 0) { - latch_ = 0; + increment_ = true; } + if constexpr (byte == 3) { + increment_ = false; + } + } + } + template uint8_t read() { + if(latched_) { + const uint8_t result = latch_[byte]; + if constexpr (byte == 0) { + latched_ = false; + } return result; } - bool advance(int count) { - // The 8250 uses a simple binary counter to replace the - // 6526's time-of-day clock. So this is easy. - const uint32_t distance_to_alarm = (alarm_ - value_) & 0xff'ffff; - const auto increment = uint32_t(count) & increment_mask_; - value_ = (value_ + increment) & 0xff'ffff; - return distance_to_alarm <= increment; + if constexpr (byte == 3) { + latched_ = true; + latch_ = value_; } + return value_[byte]; + } + + bool advance(int count) { + if(!increment_) { + return false; + } + + while(count--) { + // Increment the pre-10ths divider. + ++divider_; + if(divider_ < 5) continue; + if(divider_ < 6 && !is_50Hz) continue; + divider_ = 0; + + // Increments 10ths of a second. One BCD digit. + ++value_[0]; + if(value_[0] < 10) { + continue; + } + + // Increment seconds. Actual BCD needed from here onwards. + bcd_increment(value_[1]); + if(value_[1] != 60) { + continue; + } + value_[1] = 0; + + // Increment minutes. + bcd_increment(value_[2]); + if(value_[2] != 60) { + continue; + } + value_[2] = 0; + + // TODO: increment hours, keeping AM/PM separate? + } + + return false; // TODO: test against alarm. + } +}; + +template <> class TODStorage: public TODBase { +private: + uint32_t increment_mask_ = uint32_t(~0); + uint32_t latch_ = 0; + uint32_t value_ = 0; + uint32_t alarm_ = 0xff'ffff; + +public: + template void write(uint8_t v) { + if constexpr (byte == 3) { + return; + } + constexpr int shift = byte << 3; + + // Write to either the alarm or the current value as directed; + // writing to any part of the current value other than the LSB + // pauses incrementing until the LSB is written. + const uint32_t mask = uint32_t(~(0xff << shift)); + if(write_alarm) { + alarm_ = (alarm_ & mask) | uint32_t(v << shift); + } else { + value_ = (value_ & mask) | uint32_t(v << shift); + increment_mask_ = (byte == 0) ? uint32_t(~0) : 0; + } + } + + template uint8_t read() { + if constexpr (byte == 3) { + return 0xff; // Assumed. Just a guess. + } + constexpr int shift = byte << 3; + + if constexpr (byte == 2) { + latch_ = value_ | 0xff00'0000; + } + + const uint32_t source = latch_ ? latch_ : value_; + const uint8_t result = uint8_t((source >> shift) & 0xff); + + if constexpr (byte == 0) { + latch_ = 0; + } + + return result; + } + + bool advance(int count) { + // The 8250 uses a simple binary counter to replace the + // 6526's time-of-day clock. So this is easy. + const uint32_t distance_to_alarm = (alarm_ - value_) & 0xff'ffff; + const auto increment = uint32_t(count) & increment_mask_; + value_ = (value_ + increment) & 0xff'ffff; + return distance_to_alarm <= increment; + } }; struct MOS6526Storage { @@ -306,23 +306,23 @@ struct MOS6526Storage { return should_reload; } - private: - int pending = 0; + private: + int pending = 0; - static constexpr int ReloadInOne = 1 << 0; - static constexpr int ReloadNow = 1 << 1; + static constexpr int ReloadInOne = 1 << 0; + static constexpr int ReloadNow = 1 << 1; - static constexpr int OneShotInOne = 1 << 2; - static constexpr int OneShotNow = 1 << 3; + static constexpr int OneShotInOne = 1 << 2; + static constexpr int OneShotNow = 1 << 3; - static constexpr int ApplyClockInTwo = 1 << 4; - static constexpr int ApplyClockInOne = 1 << 5; - static constexpr int ApplyClockNow = 1 << 6; + static constexpr int ApplyClockInTwo = 1 << 4; + static constexpr int ApplyClockInOne = 1 << 5; + static constexpr int ApplyClockNow = 1 << 6; - static constexpr int TestInputInOne = 1 << 7; - static constexpr int TestInputNow = 1 << 8; + static constexpr int TestInputInOne = 1 << 7; + static constexpr int TestInputNow = 1 << 8; - static constexpr int PendingClearMask = ~(ReloadNow | OneShotNow | ApplyClockNow); + static constexpr int PendingClearMask = ~(ReloadNow | OneShotNow | ApplyClockNow); } counter_[2]; static constexpr int InterruptInOne = 1 << 0; diff --git a/Components/6532/6532.hpp b/Components/6532/6532.hpp index 23a86769b..f04a27b3e 100644 --- a/Components/6532/6532.hpp +++ b/Components/6532/6532.hpp @@ -27,163 +27,176 @@ namespace MOS { implementing bus communications as required. */ template class MOS6532 { - public: - inline void set_ram(uint16_t address, uint8_t value) { ram_[address&0x7f] = value; } - inline uint8_t get_ram(uint16_t address) { return ram_[address & 0x7f]; } +public: + inline void set_ram(uint16_t address, uint8_t value) { ram_[address&0x7f] = value; } + inline uint8_t get_ram(uint16_t address) { return ram_[address & 0x7f]; } - inline void write(int address, uint8_t value) { - const uint8_t decodedAddress = address & 0x07; - switch(decodedAddress) { - // Port output - case 0x00: case 0x02: - port_[decodedAddress / 2].output = value; - static_cast(this)->set_port_output(decodedAddress / 2, port_[decodedAddress/2].output, port_[decodedAddress / 2].output_mask); - set_port_did_change(decodedAddress / 2); - break; - case 0x01: case 0x03: - port_[decodedAddress / 2].output_mask = value; - static_cast(this)->set_port_output(decodedAddress / 2, port_[decodedAddress/2].output, port_[decodedAddress / 2].output_mask); - set_port_did_change(decodedAddress / 2); - break; + inline void write(int address, uint8_t value) { + const uint8_t decodedAddress = address & 0x07; + switch(decodedAddress) { + // Port output + case 0x00: case 0x02: + port_[decodedAddress / 2].output = value; + static_cast(this)->set_port_output( + decodedAddress / 2, + port_[decodedAddress/2].output, port_[decodedAddress / 2].output_mask + ); + set_port_did_change(decodedAddress / 2); + break; + case 0x01: case 0x03: + port_[decodedAddress / 2].output_mask = value; + static_cast(this)->set_port_output( + decodedAddress / 2, port_[decodedAddress/2].output, + port_[decodedAddress / 2].output_mask + ); + set_port_did_change(decodedAddress / 2); + break; - // The timer and edge detect control - case 0x04: case 0x05: case 0x06: case 0x07: - if(address & 0x10) { - timer_.writtenShift = timer_.activeShift = (decodedAddress - 0x04) * 3 + (decodedAddress / 0x07); // i.e. 0, 3, 6, 10 - timer_.value = (unsigned(value) << timer_.activeShift) ; - timer_.interrupt_enabled = !!(address&0x08); - interrupt_status_ &= ~InterruptFlag::Timer; - evaluate_interrupts(); - } else { - a7_interrupt_.enabled = !!(address&0x2); - a7_interrupt_.active_on_positive = !!(address & 0x01); - } - break; - } - } - - inline uint8_t read(int address) { - const uint8_t decodedAddress = address & 0x7; - switch(decodedAddress) { - // Port input - case 0x00: case 0x02: { - const int port = decodedAddress / 2; - uint8_t input = static_cast(this)->get_port_input(port); - return (input & ~port_[port].output_mask) | (port_[port].output & port_[port].output_mask); - } - break; - case 0x01: case 0x03: - return port_[decodedAddress / 2].output_mask; - break; - - // Timer and interrupt control - case 0x04: case 0x06: { - uint8_t value = uint8_t(timer_.value >> timer_.activeShift); + // The timer and edge detect control + case 0x04: case 0x05: case 0x06: case 0x07: + if(address & 0x10) { + timer_.writtenShift = timer_.activeShift = + (decodedAddress - 0x04) * 3 + (decodedAddress / 0x07); // i.e. 0, 3, 6, 10 + timer_.value = (unsigned(value) << timer_.activeShift) ; timer_.interrupt_enabled = !!(address&0x08); interrupt_status_ &= ~InterruptFlag::Timer; evaluate_interrupts(); - - if(timer_.activeShift != timer_.writtenShift) { - unsigned int shift = timer_.writtenShift - timer_.activeShift; - timer_.value = (timer_.value << shift) | ((1 << shift) - 1); - timer_.activeShift = timer_.writtenShift; - } - - return value; + } else { + a7_interrupt_.enabled = !!(address&0x2); + a7_interrupt_.active_on_positive = !!(address & 0x01); } - break; - - case 0x05: case 0x07: { - uint8_t value = interrupt_status_; - interrupt_status_ &= ~InterruptFlag::PA7; - evaluate_interrupts(); - return value; - } - break; - } - - return 0xff; + break; } + } - inline void run_for(const Cycles cycles) { - unsigned int number_of_cycles = unsigned(cycles.as_integral()); + inline uint8_t read(int address) { + const uint8_t decodedAddress = address & 0x7; + switch(decodedAddress) { + // Port input + case 0x00: case 0x02: { + const int port = decodedAddress / 2; + uint8_t input = static_cast(this)->get_port_input(port); + return (input & ~port_[port].output_mask) | (port_[port].output & port_[port].output_mask); + } + break; + case 0x01: case 0x03: + return port_[decodedAddress / 2].output_mask; + break; - // permit counting _to_ zero; counting _through_ zero initiates the other behaviour - if(timer_.value >= number_of_cycles) { - timer_.value -= number_of_cycles; - } else { - number_of_cycles -= timer_.value; - timer_.value = (0x100 - number_of_cycles) & 0xff; - timer_.activeShift = 0; - interrupt_status_ |= InterruptFlag::Timer; + // Timer and interrupt control + case 0x04: case 0x06: { + uint8_t value = uint8_t(timer_.value >> timer_.activeShift); + timer_.interrupt_enabled = !!(address&0x08); + interrupt_status_ &= ~InterruptFlag::Timer; evaluate_interrupts(); + + if(timer_.activeShift != timer_.writtenShift) { + unsigned int shift = timer_.writtenShift - timer_.activeShift; + timer_.value = (timer_.value << shift) | ((1 << shift) - 1); + timer_.activeShift = timer_.writtenShift; + } + + return value; } + break; + + case 0x05: case 0x07: { + uint8_t value = interrupt_status_; + interrupt_status_ &= ~InterruptFlag::PA7; + evaluate_interrupts(); + return value; + } + break; } - MOS6532() { - timer_.value = unsigned((rand() & 0xff) << 10); - } + return 0xff; + } - inline void set_port_did_change(int port) { - if(!port) { - uint8_t new_port_a_value = (get_port_input(0) & ~port_[0].output_mask) | (port_[0].output & port_[0].output_mask); - uint8_t difference = new_port_a_value ^ a7_interrupt_.last_port_value; - a7_interrupt_.last_port_value = new_port_a_value; - if(difference&0x80) { - if( - ((new_port_a_value&0x80) && a7_interrupt_.active_on_positive) || - (!(new_port_a_value&0x80) && !a7_interrupt_.active_on_positive) - ) { - interrupt_status_ |= InterruptFlag::PA7; - evaluate_interrupts(); - } + inline void run_for(const Cycles cycles) { + unsigned int number_of_cycles = unsigned(cycles.as_integral()); + + // permit counting _to_ zero; counting _through_ zero initiates the other behaviour + if(timer_.value >= number_of_cycles) { + timer_.value -= number_of_cycles; + } else { + number_of_cycles -= timer_.value; + timer_.value = (0x100 - number_of_cycles) & 0xff; + timer_.activeShift = 0; + interrupt_status_ |= InterruptFlag::Timer; + evaluate_interrupts(); + } + } + + MOS6532() { + timer_.value = unsigned((rand() & 0xff) << 10); + } + + inline void set_port_did_change(int port) { + if(!port) { + uint8_t new_port_a_value = + (get_port_input(0) & ~port_[0].output_mask) | + (port_[0].output & port_[0].output_mask); + uint8_t difference = new_port_a_value ^ a7_interrupt_.last_port_value; + a7_interrupt_.last_port_value = new_port_a_value; + if(difference&0x80) { + if( + ((new_port_a_value&0x80) && a7_interrupt_.active_on_positive) || + (!(new_port_a_value&0x80) && !a7_interrupt_.active_on_positive) + ) { + interrupt_status_ |= InterruptFlag::PA7; + evaluate_interrupts(); } } } + } - inline bool get_inerrupt_line() const { - return interrupt_line_; - } + inline bool get_inerrupt_line() const { + return interrupt_line_; + } - private: - uint8_t ram_[128]; +private: + uint8_t ram_[128]; - struct { - unsigned int value; - unsigned int activeShift = 10, writtenShift = 10; - bool interrupt_enabled = false; - } timer_; + struct { + unsigned int value; + unsigned int activeShift = 10, writtenShift = 10; + bool interrupt_enabled = false; + } timer_; - struct { - bool enabled = false; - bool active_on_positive = false; - uint8_t last_port_value = 0; - } a7_interrupt_; + struct { + bool enabled = false; + bool active_on_positive = false; + uint8_t last_port_value = 0; + } a7_interrupt_; - struct { - uint8_t output_mask = 0, output = 0; - } port_[2]; + struct { + uint8_t output_mask = 0, output = 0; + } port_[2]; - uint8_t interrupt_status_ = 0; - enum InterruptFlag: uint8_t { - Timer = 0x80, - PA7 = 0x40 - }; - bool interrupt_line_ = false; + uint8_t interrupt_status_ = 0; + enum InterruptFlag: uint8_t { + Timer = 0x80, + PA7 = 0x40 + }; + bool interrupt_line_ = false; - // expected to be overridden - void set_port_output([[maybe_unused]] int port, [[maybe_unused]] uint8_t value, [[maybe_unused]] uint8_t output_mask) {} - uint8_t get_port_input([[maybe_unused]] int port) { - return 0xff; - } - void set_irq_line(bool) {} + // Expected to be overridden. + void set_port_output( + [[maybe_unused]] int port, + [[maybe_unused]] uint8_t value, + [[maybe_unused]] uint8_t output_mask + ) {} + uint8_t get_port_input([[maybe_unused]] int port) { + return 0xff; + } + void set_irq_line(bool) {} - inline void evaluate_interrupts() { - interrupt_line_ = - ((interrupt_status_&InterruptFlag::Timer) && timer_.interrupt_enabled) || - ((interrupt_status_&InterruptFlag::PA7) && a7_interrupt_.enabled); - set_irq_line(interrupt_line_); - } + inline void evaluate_interrupts() { + interrupt_line_ = + ((interrupt_status_&InterruptFlag::Timer) && timer_.interrupt_enabled) || + ((interrupt_status_&InterruptFlag::PA7) && a7_interrupt_.enabled); + set_irq_line(interrupt_line_); + } }; } diff --git a/Components/6560/6560.cpp b/Components/6560/6560.cpp index 51d053994..82ee0807b 100644 --- a/Components/6560/6560.cpp +++ b/Components/6560/6560.cpp @@ -16,21 +16,21 @@ AudioGenerator::AudioGenerator(Concurrency::AsyncTaskQueue &audio_queue) audio_queue_(audio_queue) {} -void AudioGenerator::set_volume(uint8_t volume) { +void AudioGenerator::set_volume(const uint8_t volume) { audio_queue_.enqueue([this, volume]() { volume_ = int16_t(volume) * range_multiplier_; dc_offset_ = volume_ >> 4; }); } -void AudioGenerator::set_control(int channel, uint8_t value) { +void AudioGenerator::set_control(const int channel, const uint8_t value) { audio_queue_.enqueue([this, channel, value]() { control_registers_[channel] = value; }); } // Source: VICE. Not original. -static uint8_t noise_pattern[] = { +constexpr uint8_t noise_pattern[] = { 0x07, 0x1e, 0x1e, 0x1c, 0x1c, 0x3e, 0x3c, 0x38, 0x78, 0xf8, 0x7c, 0x1e, 0x1f, 0x8f, 0x07, 0x07, 0xc1, 0xc0, 0xe0, 0xf1, 0xe0, 0xf0, 0xe3, 0xe1, 0xc0, 0xe0, 0x78, 0x7e, 0x3c, 0x38, 0xe0, 0xe1, 0xc3, 0xc3, 0x87, 0xc7, 0x07, 0x1e, 0x1c, 0x1f, 0x0e, 0x0e, 0x1e, 0x0e, 0x0f, 0x0f, 0xc3, 0xc3, @@ -97,17 +97,19 @@ static uint8_t noise_pattern[] = { 0xf0, 0xe1, 0xe0, 0x78, 0x70, 0x38, 0x3c, 0x3e, 0x1e, 0x3c, 0x1e, 0x1c, 0x70, 0x3c, 0x38, 0x3f, }; -#define shift(r) shift_registers_[r] = (shift_registers_[r] << 1) | (((shift_registers_[r]^0x80)&control_registers_[r]) >> 7) +#define shift(r) shift_registers_[r] = \ + (shift_registers_[r] << 1) | (((shift_registers_[r]^0x80)&control_registers_[r]) >> 7) #define increment(r) shift_registers_[r] = (shift_registers_[r]+1)%8191 -#define update(r, m, up) counters_[r]++; if((counters_[r] >> m) == 0x80) { up(r); counters_[r] = unsigned(control_registers_[r]&0x7f) << m; } -// Note on slightly askew test: as far as I can make out, if the value in the register is 0x7f then what's supposed to happen -// is that the 0x7f is loaded, on the next clocked cycle the Vic spots a 0x7f, pumps the output, reloads, etc. No increment -// ever occurs. It's conditional. I don't really want two conditionals if I can avoid it so I'm incrementing regardless and -// testing against 0x80. The effect should be the same: loading with 0x7f means an output update every cycle, loading with 0x7e -// means every second cycle, etc. +#define update(r, m, up) \ + counters_[r]++; if((counters_[r] >> m) == 0x80) { up(r); counters_[r] = unsigned(control_registers_[r]&0x7f) << m; } +// Note on slightly askew test: as far as I can make out, if the value in the register is 0x7f then what's supposed to +// happen is that the 0x7f is loaded, on the next clocked cycle the Vic spots a 0x7f, pumps the output, reloads, etc. No +// increment ever occurs. It's conditional. I don't really want two conditionals if I can avoid it so I'm incrementing +// regardless and testing against 0x80. The effect should be the same: loading with 0x7f means an output update every +// cycle, loading with 0x7e means every second cycle, etc. template -void AudioGenerator::apply_samples(std::size_t number_of_samples, Outputs::Speaker::MonoSample *target) { +void AudioGenerator::apply_samples(const std::size_t number_of_samples, Outputs::Speaker::MonoSample *const target) { for(unsigned int c = 0; c < number_of_samples; ++c) { update(0, 2, shift); update(1, 1, shift); @@ -129,9 +131,12 @@ void AudioGenerator::apply_samples(std::size_t number_of_samples, Outputs::Speak )); } } -template void AudioGenerator::apply_samples(std::size_t, Outputs::Speaker::MonoSample *); -template void AudioGenerator::apply_samples(std::size_t, Outputs::Speaker::MonoSample *); -template void AudioGenerator::apply_samples(std::size_t, Outputs::Speaker::MonoSample *); +template void AudioGenerator::apply_samples( + std::size_t, Outputs::Speaker::MonoSample *); +template void AudioGenerator::apply_samples( + std::size_t, Outputs::Speaker::MonoSample *); +template void AudioGenerator::apply_samples( + std::size_t, Outputs::Speaker::MonoSample *); void AudioGenerator::set_sample_volume_range(std::int16_t range) { range_multiplier_ = int16_t(range / 64); diff --git a/Components/6560/6560.hpp b/Components/6560/6560.hpp index fc78cd860..61f4350e6 100644 --- a/Components/6560/6560.hpp +++ b/Components/6560/6560.hpp @@ -18,30 +18,30 @@ namespace MOS::MOS6560 { // audio state class AudioGenerator: public Outputs::Speaker::BufferSource { - public: - AudioGenerator(Concurrency::AsyncTaskQueue &audio_queue); +public: + AudioGenerator(Concurrency::AsyncTaskQueue &audio_queue); - void set_volume(uint8_t volume); - void set_control(int channel, uint8_t value); + void set_volume(uint8_t); + void set_control(int channel, uint8_t value); - // For ::SampleSource. - template - void apply_samples(std::size_t number_of_samples, Outputs::Speaker::MonoSample *target); - void set_sample_volume_range(std::int16_t range); + // For ::SampleSource. + template + void apply_samples(std::size_t number_of_samples, Outputs::Speaker::MonoSample *target); + void set_sample_volume_range(std::int16_t); - private: - Concurrency::AsyncTaskQueue &audio_queue_; +private: + Concurrency::AsyncTaskQueue &audio_queue_; - unsigned int counters_[4] = {2, 1, 0, 0}; // create a slight phase offset for the three channels - unsigned int shift_registers_[4] = {0, 0, 0, 0}; - uint8_t control_registers_[4] = {0, 0, 0, 0}; - int16_t volume_ = 0; - int16_t dc_offset_ = 0; - int16_t range_multiplier_ = 1; + unsigned int counters_[4] = {2, 1, 0, 0}; // create a slight phase offset for the three channels + unsigned int shift_registers_[4] = {0, 0, 0, 0}; + uint8_t control_registers_[4] = {0, 0, 0, 0}; + int16_t volume_ = 0; + int16_t dc_offset_ = 0; + int16_t range_multiplier_ = 1; }; struct BusHandler { - void perform_read([[maybe_unused]] uint16_t address, [[maybe_unused]] uint8_t *pixel_data, [[maybe_unused]] uint8_t *colour_data) { + void perform_read(uint16_t, uint8_t *const pixel_data, uint8_t *const colour_data) { *pixel_data = 0xff; *colour_data = 0xff; } @@ -60,462 +60,495 @@ enum class OutputMode { @c write and @c read provide register access. */ template class MOS6560 { - public: - MOS6560(BusHandler &bus_handler) : - bus_handler_(bus_handler), - crt_(65*4, 1, Outputs::Display::Type::NTSC60, Outputs::Display::InputDataType::Luminance8Phase8), - audio_generator_(audio_queue_), - speaker_(audio_generator_) - { - // default to s-video output - crt_.set_display_type(Outputs::Display::DisplayType::SVideo); +public: + MOS6560(BusHandler &bus_handler) : + bus_handler_(bus_handler), + crt_(65*4, 1, Outputs::Display::Type::NTSC60, Outputs::Display::InputDataType::Luminance8Phase8), + audio_generator_(audio_queue_), + speaker_(audio_generator_) + { + // default to s-video output + crt_.set_display_type(Outputs::Display::DisplayType::SVideo); - // default to NTSC - set_output_mode(OutputMode::NTSC); + // default to NTSC + set_output_mode(OutputMode::NTSC); + } + + ~MOS6560() { + audio_queue_.flush(); + } + + void set_clock_rate(const double clock_rate) { + speaker_.set_input_rate(float(clock_rate / 4.0)); + } + + void set_scan_target(Outputs::Display::ScanTarget *const scan_target) { + crt_.set_scan_target(scan_target); + } + Outputs::Display::ScanStatus get_scaled_scan_status() const { + return crt_.get_scaled_scan_status() / 4.0f; + } + void set_display_type(const Outputs::Display::DisplayType display_type) { + crt_.set_display_type(display_type); + } + Outputs::Display::DisplayType get_display_type() const { + return crt_.get_display_type(); + } + Outputs::Speaker::Speaker *get_speaker() { + return &speaker_; + } + + void set_high_frequency_cutoff(const float cutoff) { + speaker_.set_high_frequency_cutoff(cutoff); + } + + /*! + Sets the output mode to either PAL or NTSC. + */ + void set_output_mode(const OutputMode output_mode) { + output_mode_ = output_mode; + + // Luminances are encoded trivially: on a 0-255 scale. + const uint8_t luminances[16] = { + 0, 255, 64, 192, + 128, 128, 64, 192, + 128, 192, 128, 255, + 192, 192, 128, 255 + }; + + // Chrominances are encoded such that 0-128 is a complete revolution of phase; + // anything above 191 disables the colour subcarrier. Phase is relative to the + // colour burst, so 0 is green (NTSC) or blue/violet (PAL). + const uint8_t pal_chrominances[16] = { + 255, 255, 90, 20, + 96, 42, 8, 72, + 84, 90, 90, 20, + 96, 42, 8, 72, + }; + const uint8_t ntsc_chrominances[16] = { + 255, 255, 121, 57, + 103, 42, 80, 16, + 0, 9, 121, 57, + 103, 42, 80, 16, + }; + const uint8_t *chrominances; + Outputs::Display::Type display_type; + + switch(output_mode) { + default: + chrominances = pal_chrominances; + display_type = Outputs::Display::Type::PAL50; + timing_.cycles_per_line = 71; + timing_.line_counter_increment_offset = 4; + timing_.final_line_increment_position = + timing_.cycles_per_line - timing_.line_counter_increment_offset; + timing_.lines_per_progressive_field = 312; + timing_.supports_interlacing = false; + break; + + case OutputMode::NTSC: + chrominances = ntsc_chrominances; + display_type = Outputs::Display::Type::NTSC60; + timing_.cycles_per_line = 65; + timing_.line_counter_increment_offset = 40; + timing_.final_line_increment_position = 58; + timing_.lines_per_progressive_field = 261; + timing_.supports_interlacing = true; + break; } - ~MOS6560() { - audio_queue_.flush(); + crt_.set_new_display_type(timing_.cycles_per_line*4, display_type); + + switch(output_mode) { + case OutputMode::PAL: + crt_.set_visible_area(Outputs::Display::Rect(0.1f, 0.07f, 0.9f, 0.9f)); + break; + case OutputMode::NTSC: + crt_.set_visible_area(Outputs::Display::Rect(0.05f, 0.05f, 0.9f, 0.9f)); + break; } - void set_clock_rate(double clock_rate) { - speaker_.set_input_rate(float(clock_rate / 4.0)); + for(int c = 0; c < 16; c++) { + uint8_t *colour = reinterpret_cast(&colours_[c]); + colour[0] = luminances[c]; + colour[1] = chrominances[c]; } + } - void set_scan_target(Outputs::Display::ScanTarget *scan_target) { crt_.set_scan_target(scan_target); } - Outputs::Display::ScanStatus get_scaled_scan_status() const { return crt_.get_scaled_scan_status() / 4.0f; } - void set_display_type(Outputs::Display::DisplayType display_type) { crt_.set_display_type(display_type); } - Outputs::Display::DisplayType get_display_type() const { return crt_.get_display_type(); } - Outputs::Speaker::Speaker *get_speaker() { return &speaker_; } + /*! + Runs for cycles. Derr. + */ + inline void run_for(const Cycles cycles) { + // keep track of the amount of time since the speaker was updated; lazy updates are applied + cycles_since_speaker_update_ += cycles; - void set_high_frequency_cutoff(float cutoff) { - speaker_.set_high_frequency_cutoff(cutoff); - } + auto number_of_cycles = cycles.as_integral(); + while(number_of_cycles--) { + // keep an old copy of the vertical count because that test is a cycle later than the actual changes + int previous_vertical_counter = vertical_counter_; - /*! - Sets the output mode to either PAL or NTSC. - */ - void set_output_mode(OutputMode output_mode) { - output_mode_ = output_mode; - - // Luminances are encoded trivially: on a 0-255 scale. - const uint8_t luminances[16] = { - 0, 255, 64, 192, - 128, 128, 64, 192, - 128, 192, 128, 255, - 192, 192, 128, 255 - }; - - // Chrominances are encoded such that 0-128 is a complete revolution of phase; - // anything above 191 disables the colour subcarrier. Phase is relative to the - // colour burst, so 0 is green (NTSC) or blue/violet (PAL). - const uint8_t pal_chrominances[16] = { - 255, 255, 90, 20, - 96, 42, 8, 72, - 84, 90, 90, 20, - 96, 42, 8, 72, - }; - const uint8_t ntsc_chrominances[16] = { - 255, 255, 121, 57, - 103, 42, 80, 16, - 0, 9, 121, 57, - 103, 42, 80, 16, - }; - const uint8_t *chrominances; - Outputs::Display::Type display_type; - - switch(output_mode) { - default: - chrominances = pal_chrominances; - display_type = Outputs::Display::Type::PAL50; - timing_.cycles_per_line = 71; - timing_.line_counter_increment_offset = 4; - timing_.final_line_increment_position = timing_.cycles_per_line - timing_.line_counter_increment_offset; - timing_.lines_per_progressive_field = 312; - timing_.supports_interlacing = false; - break; - - case OutputMode::NTSC: - chrominances = ntsc_chrominances; - display_type = Outputs::Display::Type::NTSC60; - timing_.cycles_per_line = 65; - timing_.line_counter_increment_offset = 40; - timing_.final_line_increment_position = 58; - timing_.lines_per_progressive_field = 261; - timing_.supports_interlacing = true; - break; - } - - crt_.set_new_display_type(timing_.cycles_per_line*4, display_type); - - switch(output_mode) { - case OutputMode::PAL: - crt_.set_visible_area(Outputs::Display::Rect(0.1f, 0.07f, 0.9f, 0.9f)); - break; - case OutputMode::NTSC: - crt_.set_visible_area(Outputs::Display::Rect(0.05f, 0.05f, 0.9f, 0.9f)); - break; - } - - for(int c = 0; c < 16; c++) { - uint8_t *colour = reinterpret_cast(&colours_[c]); - colour[0] = luminances[c]; - colour[1] = chrominances[c]; - } - } - - /*! - Runs for cycles. Derr. - */ - inline void run_for(const Cycles cycles) { - // keep track of the amount of time since the speaker was updated; lazy updates are applied - cycles_since_speaker_update_ += cycles; - - auto number_of_cycles = cycles.as_integral(); - while(number_of_cycles--) { - // keep an old copy of the vertical count because that test is a cycle later than the actual changes - int previous_vertical_counter = vertical_counter_; - - // keep track of internal time relative to this scanline - ++horizontal_counter_; - if(horizontal_counter_ == timing_.cycles_per_line) { - if(horizontal_drawing_latch_) { - ++current_character_row_; - if( - (current_character_row_ == 16) || - (current_character_row_ == 8 && !registers_.tall_characters) - ) { - current_character_row_ = 0; - ++current_row_; - } - - pixel_line_cycle_ = -1; - columns_this_line_ = -1; - column_counter_ = -1; - } - - horizontal_counter_ = 0; - if(output_mode_ == OutputMode::PAL) is_odd_line_ ^= true; - horizontal_drawing_latch_ = false; - - ++vertical_counter_; - if(vertical_counter_ == lines_this_field()) { - vertical_counter_ = 0; - - if(output_mode_ == OutputMode::NTSC) is_odd_frame_ ^= true; - current_row_ = 0; - rows_this_field_ = -1; - vertical_drawing_latch_ = false; - base_video_matrix_address_counter_ = 0; + // keep track of internal time relative to this scanline + ++horizontal_counter_; + if(horizontal_counter_ == timing_.cycles_per_line) { + if(horizontal_drawing_latch_) { + ++current_character_row_; + if( + (current_character_row_ == 16) || + (current_character_row_ == 8 && !registers_.tall_characters) + ) { current_character_row_ = 0; + ++current_row_; } + + pixel_line_cycle_ = -1; + columns_this_line_ = -1; + column_counter_ = -1; } - // check for vertical starting events - vertical_drawing_latch_ |= registers_.first_row_location == (previous_vertical_counter >> 1); - horizontal_drawing_latch_ |= vertical_drawing_latch_ && (horizontal_counter_ == registers_.first_column_location); + horizontal_counter_ = 0; + if(output_mode_ == OutputMode::PAL) is_odd_line_ ^= true; + horizontal_drawing_latch_ = false; - if(pixel_line_cycle_ >= 0) ++pixel_line_cycle_; - switch(pixel_line_cycle_) { - case -1: - if(horizontal_drawing_latch_) { - pixel_line_cycle_ = 0; - video_matrix_address_counter_ = base_video_matrix_address_counter_; - } + ++vertical_counter_; + if(vertical_counter_ == lines_this_field()) { + vertical_counter_ = 0; + + if(output_mode_ == OutputMode::NTSC) is_odd_frame_ ^= true; + current_row_ = 0; + rows_this_field_ = -1; + vertical_drawing_latch_ = false; + base_video_matrix_address_counter_ = 0; + current_character_row_ = 0; + } + } + + // check for vertical starting events + vertical_drawing_latch_ |= registers_.first_row_location == (previous_vertical_counter >> 1); + horizontal_drawing_latch_ |= + vertical_drawing_latch_ && (horizontal_counter_ == registers_.first_column_location); + + if(pixel_line_cycle_ >= 0) ++pixel_line_cycle_; + switch(pixel_line_cycle_) { + case -1: + if(horizontal_drawing_latch_) { + pixel_line_cycle_ = 0; + video_matrix_address_counter_ = base_video_matrix_address_counter_; + } + break; + case 1: columns_this_line_ = registers_.number_of_columns; break; + case 2: if(rows_this_field_ < 0) rows_this_field_ = registers_.number_of_rows; break; + case 3: if(current_row_ < rows_this_field_) column_counter_ = 0; break; + } + + uint16_t fetch_address = 0x1c; + if(column_counter_ >= 0 && column_counter_ < columns_this_line_*2) { + if(column_counter_&1) { + fetch_address = + registers_.character_cell_start_address + + (character_code_*(registers_.tall_characters ? 16 : 8)) + + current_character_row_; + } else { + fetch_address = uint16_t(registers_.video_matrix_start_address + video_matrix_address_counter_); + ++video_matrix_address_counter_; + if( + (current_character_row_ == 15) || + (current_character_row_ == 7 && !registers_.tall_characters) + ) { + base_video_matrix_address_counter_ = video_matrix_address_counter_; + } + } + } + + fetch_address &= 0x3fff; + + uint8_t pixel_data; + uint8_t colour_data; + bus_handler_.perform_read(fetch_address, &pixel_data, &colour_data); + + // TODO: there should be a further two-cycle delay on pixels being output; the reverse bit should + // divide the byte it is set for 3:1 and then continue as usual. + + // determine output state; colour burst and sync timing are currently a guess + State this_state; + if(horizontal_counter_ > timing_.cycles_per_line-4) this_state = State::ColourBurst; + else if(horizontal_counter_ > timing_.cycles_per_line-7) this_state = State::Sync; + else { + this_state = (column_counter_ >= 0 && column_counter_ < columns_this_line_*2) ? + State::Pixels : State::Border; + } + + // apply vertical sync + if( + (vertical_counter_ < 3 && is_odd_frame()) || + (registers_.interlaced && + ( + (vertical_counter_ == 0 && horizontal_counter_ > 32) || + (vertical_counter_ == 1) || (vertical_counter_ == 2) || + (vertical_counter_ == 3 && horizontal_counter_ <= 32) + ) + )) + this_state = State::Sync; + + // update the CRT + if(this_state != output_state_) { + switch(output_state_) { + case State::Sync: + crt_.output_sync(cycles_in_state_ * 4); + break; + case State::ColourBurst: + crt_.output_colour_burst(cycles_in_state_ * 4, (is_odd_frame_ || is_odd_line_) ? 128 : 0); + break; + case State::Border: + crt_.output_level(cycles_in_state_ * 4, registers_.border_colour); + break; + case State::Pixels: + crt_.output_data(cycles_in_state_ * 4); break; - case 1: columns_this_line_ = registers_.number_of_columns; break; - case 2: if(rows_this_field_ < 0) rows_this_field_ = registers_.number_of_rows; break; - case 3: if(current_row_ < rows_this_field_) column_counter_ = 0; break; } + output_state_ = this_state; + cycles_in_state_ = 0; - uint16_t fetch_address = 0x1c; - if(column_counter_ >= 0 && column_counter_ < columns_this_line_*2) { - if(column_counter_&1) { - fetch_address = registers_.character_cell_start_address + (character_code_*(registers_.tall_characters ? 16 : 8)) + current_character_row_; - } else { - fetch_address = uint16_t(registers_.video_matrix_start_address + video_matrix_address_counter_); - ++video_matrix_address_counter_; - if( - (current_character_row_ == 15) || - (current_character_row_ == 7 && !registers_.tall_characters) - ) { - base_video_matrix_address_counter_ = video_matrix_address_counter_; - } - } - } - - fetch_address &= 0x3fff; - - uint8_t pixel_data; - uint8_t colour_data; - bus_handler_.perform_read(fetch_address, &pixel_data, &colour_data); - - // TODO: there should be a further two-cycle delay on pixels being output; the reverse bit should - // divide the byte it is set for 3:1 and then continue as usual. - - // determine output state; colour burst and sync timing are currently a guess - State this_state; - if(horizontal_counter_ > timing_.cycles_per_line-4) this_state = State::ColourBurst; - else if(horizontal_counter_ > timing_.cycles_per_line-7) this_state = State::Sync; - else { - this_state = (column_counter_ >= 0 && column_counter_ < columns_this_line_*2) ? State::Pixels : State::Border; - } - - // apply vertical sync - if( - (vertical_counter_ < 3 && is_odd_frame()) || - (registers_.interlaced && - ( - (vertical_counter_ == 0 && horizontal_counter_ > 32) || - (vertical_counter_ == 1) || (vertical_counter_ == 2) || - (vertical_counter_ == 3 && horizontal_counter_ <= 32) - ) - )) - this_state = State::Sync; - - // update the CRT - if(this_state != output_state_) { - switch(output_state_) { - case State::Sync: crt_.output_sync(cycles_in_state_ * 4); break; - case State::ColourBurst: crt_.output_colour_burst(cycles_in_state_ * 4, (is_odd_frame_ || is_odd_line_) ? 128 : 0); break; - case State::Border: crt_.output_level(cycles_in_state_ * 4, registers_.border_colour); break; - case State::Pixels: crt_.output_data(cycles_in_state_ * 4); break; - } - output_state_ = this_state; - cycles_in_state_ = 0; - - pixel_pointer = nullptr; - if(output_state_ == State::Pixels) { - pixel_pointer = reinterpret_cast(crt_.begin_data(260)); - } - } - ++cycles_in_state_; - + pixel_pointer = nullptr; if(output_state_ == State::Pixels) { - // TODO: palette changes can happen within half-characters; the below needs to be divided. - // Also: a perfect opportunity to rearrange this inner loop for no longer needing to be - // two parts with a cooperative owner? - if(column_counter_&1) { - character_value_ = pixel_data; + pixel_pointer = reinterpret_cast(crt_.begin_data(260)); + } + } + ++cycles_in_state_; - if(pixel_pointer) { - uint16_t cell_colour = colours_[character_colour_ & 0x7]; - if(!(character_colour_&0x8)) { - uint16_t colours[2]; - if(registers_.invertedCells) { - colours[0] = cell_colour; - colours[1] = registers_.background_colour; - } else { - colours[0] = registers_.background_colour; - colours[1] = cell_colour; - } - pixel_pointer[0] = colours[(character_value_ >> 7)&1]; - pixel_pointer[1] = colours[(character_value_ >> 6)&1]; - pixel_pointer[2] = colours[(character_value_ >> 5)&1]; - pixel_pointer[3] = colours[(character_value_ >> 4)&1]; - pixel_pointer[4] = colours[(character_value_ >> 3)&1]; - pixel_pointer[5] = colours[(character_value_ >> 2)&1]; - pixel_pointer[6] = colours[(character_value_ >> 1)&1]; - pixel_pointer[7] = colours[(character_value_ >> 0)&1]; + if(output_state_ == State::Pixels) { + // TODO: palette changes can happen within half-characters; the below needs to be divided. + // Also: a perfect opportunity to rearrange this inner loop for no longer needing to be + // two parts with a cooperative owner? + if(column_counter_&1) { + character_value_ = pixel_data; + + if(pixel_pointer) { + const uint16_t cell_colour = colours_[character_colour_ & 0x7]; + if(!(character_colour_&0x8)) { + uint16_t colours[2]; + if(registers_.invertedCells) { + colours[0] = cell_colour; + colours[1] = registers_.background_colour; } else { - uint16_t colours[4] = {registers_.background_colour, registers_.border_colour, cell_colour, registers_.auxiliary_colour}; - pixel_pointer[0] = - pixel_pointer[1] = colours[(character_value_ >> 6)&3]; - pixel_pointer[2] = - pixel_pointer[3] = colours[(character_value_ >> 4)&3]; - pixel_pointer[4] = - pixel_pointer[5] = colours[(character_value_ >> 2)&3]; - pixel_pointer[6] = - pixel_pointer[7] = colours[(character_value_ >> 0)&3]; + colours[0] = registers_.background_colour; + colours[1] = cell_colour; } - - pixel_pointer += 8; + pixel_pointer[0] = colours[(character_value_ >> 7)&1]; + pixel_pointer[1] = colours[(character_value_ >> 6)&1]; + pixel_pointer[2] = colours[(character_value_ >> 5)&1]; + pixel_pointer[3] = colours[(character_value_ >> 4)&1]; + pixel_pointer[4] = colours[(character_value_ >> 3)&1]; + pixel_pointer[5] = colours[(character_value_ >> 2)&1]; + pixel_pointer[6] = colours[(character_value_ >> 1)&1]; + pixel_pointer[7] = colours[(character_value_ >> 0)&1]; + } else { + const uint16_t colours[4] = { + registers_.background_colour, + registers_.border_colour, + cell_colour, + registers_.auxiliary_colour + }; + pixel_pointer[0] = + pixel_pointer[1] = colours[(character_value_ >> 6)&3]; + pixel_pointer[2] = + pixel_pointer[3] = colours[(character_value_ >> 4)&3]; + pixel_pointer[4] = + pixel_pointer[5] = colours[(character_value_ >> 2)&3]; + pixel_pointer[6] = + pixel_pointer[7] = colours[(character_value_ >> 0)&3]; } - } else { - character_code_ = pixel_data; - character_colour_ = colour_data; + + pixel_pointer += 8; } - } - - // Keep counting columns even if sync or the colour burst have interceded. - if(column_counter_ >= 0 && column_counter_ < columns_this_line_*2) { - ++column_counter_; + } else { + character_code_ = pixel_data; + character_colour_ = colour_data; } } + + // Keep counting columns even if sync or the colour burst have interceded. + if(column_counter_ >= 0 && column_counter_ < columns_this_line_*2) { + ++column_counter_; + } } + } - /*! - Causes the 6560 to flush as much pending CRT and speaker communications as possible. - */ - inline void flush() { - update_audio(); - audio_queue_.perform(); - } + /*! + Causes the 6560 to flush as much pending CRT and speaker communications as possible. + */ + inline void flush() { + update_audio(); + audio_queue_.perform(); + } - /*! - Writes to a 6560 register. - */ - void write(int address, uint8_t value) { - address &= 0xf; - registers_.direct_values[address] = value; - switch(address) { - case 0x0: - registers_.interlaced = !!(value&0x80) && timing_.supports_interlacing; - registers_.first_column_location = value & 0x7f; - break; + /*! + Writes to a 6560 register. + */ + void write(int address, const uint8_t value) { + address &= 0xf; + registers_.direct_values[address] = value; + switch(address) { + case 0x0: + registers_.interlaced = !!(value&0x80) && timing_.supports_interlacing; + registers_.first_column_location = value & 0x7f; + break; - case 0x1: - registers_.first_row_location = value; - break; + case 0x1: + registers_.first_row_location = value; + break; - case 0x2: - registers_.number_of_columns = value & 0x7f; - registers_.video_matrix_start_address = uint16_t((registers_.video_matrix_start_address & 0x3c00) | ((value & 0x80) << 2)); - break; + case 0x2: + registers_.number_of_columns = value & 0x7f; + registers_.video_matrix_start_address = uint16_t( + (registers_.video_matrix_start_address & 0x3c00) | ((value & 0x80) << 2) + ); + break; - case 0x3: - registers_.number_of_rows = (value >> 1)&0x3f; - registers_.tall_characters = !!(value&0x01); - break; + case 0x3: + registers_.number_of_rows = (value >> 1)&0x3f; + registers_.tall_characters = !!(value&0x01); + break; - case 0x5: - registers_.character_cell_start_address = uint16_t((value & 0x0f) << 10); - registers_.video_matrix_start_address = uint16_t((registers_.video_matrix_start_address & 0x0200) | ((value & 0xf0) << 6)); - break; + case 0x5: + registers_.character_cell_start_address = uint16_t((value & 0x0f) << 10); + registers_.video_matrix_start_address = uint16_t( + (registers_.video_matrix_start_address & 0x0200) | ((value & 0xf0) << 6) + ); + break; - case 0xa: - case 0xb: - case 0xc: - case 0xd: - update_audio(); - audio_generator_.set_control(address - 0xa, value); - break; + case 0xa: + case 0xb: + case 0xc: + case 0xd: + update_audio(); + audio_generator_.set_control(address - 0xa, value); + break; - case 0xe: - update_audio(); - registers_.auxiliary_colour = colours_[value >> 4]; - audio_generator_.set_volume(value & 0xf); - break; + case 0xe: + update_audio(); + registers_.auxiliary_colour = colours_[value >> 4]; + audio_generator_.set_volume(value & 0xf); + break; - case 0xf: { - const uint16_t new_border_colour = colours_[value & 0x07]; - if(new_border_colour != registers_.border_colour) { - if(output_state_ == State::Border) { - crt_.output_level(cycles_in_state_ * 4, registers_.border_colour); - cycles_in_state_ = 0; - } - registers_.border_colour = new_border_colour; + case 0xf: { + const uint16_t new_border_colour = colours_[value & 0x07]; + if(new_border_colour != registers_.border_colour) { + if(output_state_ == State::Border) { + crt_.output_level(cycles_in_state_ * 4, registers_.border_colour); + cycles_in_state_ = 0; } - registers_.invertedCells = !((value >> 3)&1); - registers_.background_colour = colours_[value >> 4]; + registers_.border_colour = new_border_colour; } - break; - - // TODO: the lightpen, etc - - default: - break; + registers_.invertedCells = !((value >> 3)&1); + registers_.background_colour = colours_[value >> 4]; } + break; + + // TODO: the lightpen, etc + + default: + break; } + } - /* - Reads from a 6560 register. - */ - uint8_t read(int address) const { - address &= 0xf; - switch(address) { - default: return registers_.direct_values[address]; - case 0x03: return uint8_t(raster_value() << 7) | (registers_.direct_values[3] & 0x7f); - case 0x04: return (raster_value() >> 1) & 0xff; - } + /* + Reads from a 6560 register. + */ + uint8_t read(int address) const { + address &= 0xf; + switch(address) { + default: return registers_.direct_values[address]; + case 0x03: return uint8_t(raster_value() << 7) | (registers_.direct_values[3] & 0x7f); + case 0x04: return (raster_value() >> 1) & 0xff; } + } - private: - BusHandler &bus_handler_; - Outputs::CRT::CRT crt_; +private: + BusHandler &bus_handler_; + Outputs::CRT::CRT crt_; - Concurrency::AsyncTaskQueue audio_queue_; - AudioGenerator audio_generator_; - Outputs::Speaker::PullLowpass speaker_; + Concurrency::AsyncTaskQueue audio_queue_; + AudioGenerator audio_generator_; + Outputs::Speaker::PullLowpass speaker_; - Cycles cycles_since_speaker_update_; - void update_audio() { - speaker_.run_for(audio_queue_, Cycles(cycles_since_speaker_update_.divide(Cycles(4)))); + Cycles cycles_since_speaker_update_; + void update_audio() { + speaker_.run_for(audio_queue_, Cycles(cycles_since_speaker_update_.divide(Cycles(4)))); + } + + // register state + struct { + bool interlaced = false, tall_characters = false; + uint8_t first_column_location = 0, first_row_location = 0; + uint8_t number_of_columns = 0, number_of_rows = 0; + uint16_t character_cell_start_address = 0, video_matrix_start_address = 0; + uint16_t border_colour = 0; + uint16_t background_colour = 0; + uint16_t auxiliary_colour = 0; + bool invertedCells = false; + + uint8_t direct_values[16]{}; + } registers_; + + // output state + enum State { + Sync, ColourBurst, Border, Pixels + } output_state_ = State::Sync; + int cycles_in_state_ = 0; + + // counters that cover an entire field + int horizontal_counter_ = 0, vertical_counter_ = 0; + int lines_this_field() const { + // Necessary knowledge here: only the NTSC 6560 supports interlaced video. + return registers_.interlaced ? (is_odd_frame_ ? 262 : 263) : timing_.lines_per_progressive_field; + } + int raster_value() const { + const int bonus_line = (horizontal_counter_ + timing_.line_counter_increment_offset) / timing_.cycles_per_line; + const int line = vertical_counter_ + bonus_line; + const int final_line = lines_this_field(); + + if(line < final_line) + return line; + + if(is_odd_frame()) { + return (horizontal_counter_ >= timing_.final_line_increment_position) ? 0 : final_line - 1; + } else { + return line % final_line; } + // Cf. http://www.sleepingelephant.com/ipw-web/bulletin/bb/viewtopic.php?f=14&t=7237&start=15#p80737 + } + bool is_odd_frame() const { + return is_odd_frame_ || !registers_.interlaced; + } - // register state - struct { - bool interlaced = false, tall_characters = false; - uint8_t first_column_location = 0, first_row_location = 0; - uint8_t number_of_columns = 0, number_of_rows = 0; - uint16_t character_cell_start_address = 0, video_matrix_start_address = 0; - uint16_t border_colour = 0; - uint16_t background_colour = 0; - uint16_t auxiliary_colour = 0; - bool invertedCells = false; + // latches dictating start and length of drawing + bool vertical_drawing_latch_ = false, horizontal_drawing_latch_ = false; + int rows_this_field_ = 0, columns_this_line_ = 0; - uint8_t direct_values[16]{}; - } registers_; + // current drawing position counter + int pixel_line_cycle_ = 0, column_counter_ = 0; + int current_row_ = 0; + uint16_t current_character_row_ = 0; + uint16_t video_matrix_address_counter_ = 0, base_video_matrix_address_counter_ = 0; - // output state - enum State { - Sync, ColourBurst, Border, Pixels - } output_state_ = State::Sync; - int cycles_in_state_ = 0; + // data latched from the bus + uint8_t character_code_ = 0, character_colour_ = 0, character_value_ = 0; - // counters that cover an entire field - int horizontal_counter_ = 0, vertical_counter_ = 0; - int lines_this_field() const { - // Necessary knowledge here: only the NTSC 6560 supports interlaced video. - return registers_.interlaced ? (is_odd_frame_ ? 262 : 263) : timing_.lines_per_progressive_field; - } - int raster_value() const { - const int bonus_line = (horizontal_counter_ + timing_.line_counter_increment_offset) / timing_.cycles_per_line; - const int line = vertical_counter_ + bonus_line; - const int final_line = lines_this_field(); + bool is_odd_frame_ = false, is_odd_line_ = false; - if(line < final_line) - return line; + // lookup table from 6560 colour index to appropriate PAL/NTSC value + uint16_t colours_[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - if(is_odd_frame()) { - return (horizontal_counter_ >= timing_.final_line_increment_position) ? 0 : final_line - 1; - } else { - return line % final_line; - } - // Cf. http://www.sleepingelephant.com/ipw-web/bulletin/bb/viewtopic.php?f=14&t=7237&start=15#p80737 - } - bool is_odd_frame() const { - return is_odd_frame_ || !registers_.interlaced; - } + uint16_t *pixel_pointer = nullptr; - // latches dictating start and length of drawing - bool vertical_drawing_latch_ = false, horizontal_drawing_latch_ = false; - int rows_this_field_ = 0, columns_this_line_ = 0; - - // current drawing position counter - int pixel_line_cycle_ = 0, column_counter_ = 0; - int current_row_ = 0; - uint16_t current_character_row_ = 0; - uint16_t video_matrix_address_counter_ = 0, base_video_matrix_address_counter_ = 0; - - // data latched from the bus - uint8_t character_code_ = 0, character_colour_ = 0, character_value_ = 0; - - bool is_odd_frame_ = false, is_odd_line_ = false; - - // lookup table from 6560 colour index to appropriate PAL/NTSC value - uint16_t colours_[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - - uint16_t *pixel_pointer = nullptr; - - struct { - int cycles_per_line = 0; - int line_counter_increment_offset = 0; - int final_line_increment_position = 0; - int lines_per_progressive_field = 0; - bool supports_interlacing = 0; - } timing_; - OutputMode output_mode_ = OutputMode::NTSC; + struct { + int cycles_per_line = 0; + int line_counter_increment_offset = 0; + int final_line_increment_position = 0; + int lines_per_progressive_field = 0; + bool supports_interlacing = 0; + } timing_; + OutputMode output_mode_ = OutputMode::NTSC; }; } diff --git a/Components/6845/CRTC6845.hpp b/Components/6845/CRTC6845.hpp index b3710027d..2f7669e40 100644 --- a/Components/6845/CRTC6845.hpp +++ b/Components/6845/CRTC6845.hpp @@ -63,371 +63,376 @@ enum class CursorType { // TODO UM6845R and R12/R13; see http://www.cpcwiki.eu/index.php/CRTC#CRTC_Differences template class CRTC6845 { - public: - CRTC6845(BusHandlerT &bus_handler) noexcept : - bus_handler_(bus_handler), status_(0) {} +public: + CRTC6845(BusHandlerT &bus_handler) noexcept : + bus_handler_(bus_handler), status_(0) {} - void select_register(uint8_t r) { - selected_register_ = r; + void select_register(uint8_t r) { + selected_register_ = r; + } + + uint8_t get_status() { + switch(personality) { + case Personality::UM6845R: return status_ | (bus_state_.vsync ? 0x20 : 0x00); + case Personality::AMS40226: return get_register(); + default: return 0xff; } + return 0xff; + } - uint8_t get_status() { - switch(personality) { - case Personality::UM6845R: return status_ | (bus_state_.vsync ? 0x20 : 0x00); - case Personality::AMS40226: return get_register(); - default: return 0xff; - } - return 0xff; - } + uint8_t get_register() { + if(selected_register_ == 31) status_ &= ~0x80; + if(selected_register_ == 16 || selected_register_ == 17) status_ &= ~0x40; - uint8_t get_register() { - if(selected_register_ == 31) status_ &= ~0x80; - if(selected_register_ == 16 || selected_register_ == 17) status_ &= ~0x40; + if(personality == Personality::UM6845R && selected_register_ == 31) return dummy_register_; + if(selected_register_ < 12 || selected_register_ > 17) return 0xff; + return registers_[selected_register_]; + } - if(personality == Personality::UM6845R && selected_register_ == 31) return dummy_register_; - if(selected_register_ < 12 || selected_register_ > 17) return 0xff; - return registers_[selected_register_]; - } + void set_register(const uint8_t value) { + static constexpr bool is_ega = is_egavga(personality); - void set_register(uint8_t value) { - static constexpr bool is_ega = is_egavga(personality); - - const auto load_low = [value](uint16_t &target) { - target = (target & 0xff00) | value; - }; - const auto load_high = [value](uint16_t &target) { - constexpr uint8_t mask = RefreshMask >> 8; - target = uint16_t((target & 0x00ff) | ((value & mask) << 8)); - }; - - switch(selected_register_) { - case 0: layout_.horizontal.total = value; break; - case 1: layout_.horizontal.displayed = value; break; - case 2: layout_.horizontal.start_sync = value; break; - case 3: - layout_.horizontal.sync_width = value & 0xf; - layout_.vertical.sync_lines = value >> 4; - // TODO: vertical sync lines: - // "(0 means 16 on some CRTC. Not present on all CRTCs, fixed to 16 lines on these)" - break; - case 4: layout_.vertical.total = value & 0x7f; break; - case 5: layout_.vertical.adjust = value & 0x1f; break; - case 6: layout_.vertical.displayed = value & 0x7f; break; - case 7: layout_.vertical.start_sync = value & 0x7f; break; - case 8: - switch(value & 3) { - default: layout_.interlace_mode_ = InterlaceMode::Off; break; - case 0b01: layout_.interlace_mode_ = InterlaceMode::InterlaceSync; break; - case 0b11: layout_.interlace_mode_ = InterlaceMode::InterlaceSyncAndVideo; break; - } - - // Per CPC documentation, skew doesn't work on a "type 1 or 2", i.e. an MC6845 or a UM6845R. - if(personality != Personality::UM6845R && personality != Personality::MC6845) { - switch((value >> 4)&3) { - default: display_skew_mask_ = 1; break; - case 1: display_skew_mask_ = 2; break; - case 2: display_skew_mask_ = 4; break; - } - } - break; - case 9: layout_.vertical.end_row = value & 0x1f; break; - case 10: - layout_.vertical.start_cursor = value & 0x1f; - layout_.cursor_flags = (value >> 5) & 3; - break; - case 11: - layout_.vertical.end_cursor = value & 0x1f; - break; - case 12: load_high(layout_.start_address); break; - case 13: load_low(layout_.start_address); break; - case 14: load_high(layout_.cursor_address); break; - case 15: load_low(layout_.cursor_address); break; - } - - static constexpr uint8_t masks[] = { - 0xff, // Horizontal total. - 0xff, // Horizontal display end. - 0xff, // Start horizontal blank. - 0xff, // - // EGA: b0–b4: end of horizontal blank; - // b5–b6: "Number of character clocks to delay start of display after Horizontal Total has been reached." - - is_ega ? 0xff : 0x7f, // Start horizontal retrace. - 0x1f, 0x7f, 0x7f, - 0xff, 0x1f, 0x7f, 0x1f, - uint8_t(RefreshMask >> 8), uint8_t(RefreshMask), - uint8_t(RefreshMask >> 8), uint8_t(RefreshMask), - }; - - if(selected_register_ < 16) { - registers_[selected_register_] = value & masks[selected_register_]; - } - if(selected_register_ == 31 && personality == Personality::UM6845R) { - dummy_register_ = value; - } - } - - void trigger_light_pen() { - registers_[17] = bus_state_.refresh_address & 0xff; - registers_[16] = bus_state_.refresh_address >> 8; - status_ |= 0x40; - } - - void run_for(Cycles cycles) { - auto cyles_remaining = cycles.as_integral(); - while(cyles_remaining--) { - // Intention of code below: all conditionals are evaluated as if functional; they should be - // ordered so that whatever assignments result don't affect any subsequent conditionals - - - // Do bus work. - bus_state_.cursor = is_cursor_line_ && - bus_state_.refresh_address == layout_.cursor_address; - bus_state_.display_enable = character_is_visible_ && line_is_visible_; - bus_handler_.perform_bus_cycle(bus_state_); - - // - // Shared, stateless signals. - // - const bool character_total_hit = character_counter_ == layout_.horizontal.total; - const uint8_t lines_per_row = layout_.interlace_mode_ == InterlaceMode::InterlaceSyncAndVideo ? layout_.vertical.end_row & ~1 : layout_.vertical.end_row; - const bool row_end_hit = bus_state_.row_address == lines_per_row && !is_in_adjustment_period_; - const bool was_eof = eof_latched_; - const bool new_frame = - character_total_hit && was_eof && - ( - layout_.interlace_mode_ == InterlaceMode::Off || - !odd_field_ - ); - - // - // Horizontal. - // - - // Update horizontal sync. - if(bus_state_.hsync) { - ++hsync_counter_; - bus_state_.hsync = hsync_counter_ != layout_.horizontal.sync_width; - } - if(character_counter_ == layout_.horizontal.start_sync) { - hsync_counter_ = 0; - bus_state_.hsync = true; - } - - // Check for end-of-line. - character_reset_history_ <<= 1; - if(character_total_hit) { - character_counter_ = 0; - character_is_visible_ = true; - character_reset_history_ |= 1; - } else { - character_counter_++; - } - - // Check for end of visible characters. - if(character_counter_ == layout_.horizontal.displayed) { - character_is_visible_ = false; - } - - // - // End-of-frame. - // - - if(character_total_hit) { - if(was_eof) { - eof_latched_ = eom_latched_ = is_in_adjustment_period_ = false; - adjustment_counter_ = 0; - } else if(is_in_adjustment_period_) { - adjustment_counter_ = (adjustment_counter_ + 1) & 31; - } - } - - if(character_reset_history_ & 2) { - eom_latched_ |= row_end_hit && row_counter_ == layout_.vertical.total; - } - - if(character_reset_history_ & 4 && eom_latched_) { - // TODO: I don't believe the "add 1 for interlaced" test here is accurate; others represent the extra scanline as - // additional state, presumably because adjust total might be reprogrammed at any time. - const auto adjust_length = layout_.vertical.adjust + (layout_.interlace_mode_ != InterlaceMode::Off && odd_field_ ? 1 : 0); - is_in_adjustment_period_ |= adjustment_counter_ != adjust_length; - eof_latched_ |= adjustment_counter_ == adjust_length; - } - - // - // Vertical. - // - - // Sync. - const bool vsync_horizontal = - (!odd_field_ && !character_counter_) || - (odd_field_ && character_counter_ == (layout_.horizontal.total >> 1)); - if(vsync_horizontal) { - if((row_counter_ == layout_.vertical.start_sync && !bus_state_.row_address) || bus_state_.vsync) { - bus_state_.vsync = true; - vsync_counter_ = (vsync_counter_ + 1) & 0xf; - } else { - vsync_counter_ = 0; - } - - if(vsync_counter_ == layout_.vertical.sync_lines) { - bus_state_.vsync = false; - } - } - - // Row address. - if(character_total_hit) { - if(was_eof) { - bus_state_.row_address = 0; - eof_latched_ = eom_latched_ = false; - } else if(row_end_hit) { - bus_state_.row_address = 0; - } else if(layout_.interlace_mode_ == InterlaceMode::InterlaceSyncAndVideo) { - bus_state_.row_address = (bus_state_.row_address + 2) & ~1 & 31; - } else { - bus_state_.row_address = (bus_state_.row_address + 1) & 31; - } - } - - // Row counter. - row_counter_ = next_row_counter_; - if(new_frame) { - next_row_counter_ = 0; - is_first_scanline_ = true; - } else { - next_row_counter_ = row_end_hit && character_total_hit ? (next_row_counter_ + 1) : next_row_counter_; - is_first_scanline_ &= !row_end_hit; - } - - // Vertical display enable. - if(is_first_scanline_) { - line_is_visible_ = true; - odd_field_ = bus_state_.field_count & 1; - } else if(line_is_visible_ && row_counter_ == layout_.vertical.displayed) { - line_is_visible_ = false; - ++bus_state_.field_count; - } - - - // Cursor. - if constexpr (cursor_type != CursorType::None) { - // Check for cursor enable. - is_cursor_line_ |= bus_state_.row_address == layout_.vertical.start_cursor; - is_cursor_line_ &= bus_state_.row_address != layout_.vertical.end_cursor; - - switch(cursor_type) { - // MDA-style blinking. - // https://retrocomputing.stackexchange.com/questions/27803/what-are-the-blinking-rates-of-the-caret-and-of-blinking-text-on-pc-graphics-car - // gives an 8/8 pattern for regular blinking though mode 11 is then just a guess. - case CursorType::MDA: - switch(layout_.cursor_flags) { - case 0b11: is_cursor_line_ &= (bus_state_.field_count & 8) < 3; break; - case 0b00: is_cursor_line_ &= bool(bus_state_.field_count & 8); break; - case 0b01: is_cursor_line_ = false; break; - case 0b10: is_cursor_line_ = true; break; - default: break; - } - break; - } - } - - // - // Addressing. - // - - if(new_frame) { - bus_state_.refresh_address = layout_.start_address; - } else if(character_total_hit) { - bus_state_.refresh_address = line_address_; - } else { - bus_state_.refresh_address = (bus_state_.refresh_address + 1) & RefreshMask; - } - - if(new_frame) { - line_address_ = layout_.start_address; - } else if(character_counter_ == layout_.horizontal.displayed && row_end_hit) { - line_address_ = bus_state_.refresh_address; - } - } - } - - const BusState &get_bus_state() const { - return bus_state_; - } - - private: - static constexpr uint16_t RefreshMask = (personality >= Personality::EGA) ? 0xffff : 0x3fff; - - BusHandlerT &bus_handler_; - BusState bus_state_; - - enum class InterlaceMode { - Off, - InterlaceSync, - InterlaceSyncAndVideo, + const auto load_low = [value](uint16_t &target) { + target = (target & 0xff00) | value; }; - enum class BlinkMode { - // TODO. + const auto load_high = [value](uint16_t &target) { + constexpr uint8_t mask = RefreshMask >> 8; + target = uint16_t((target & 0x00ff) | ((value & mask) << 8)); }; + + switch(selected_register_) { + case 0: layout_.horizontal.total = value; break; + case 1: layout_.horizontal.displayed = value; break; + case 2: layout_.horizontal.start_sync = value; break; + case 3: + layout_.horizontal.sync_width = value & 0xf; + layout_.vertical.sync_lines = value >> 4; + // TODO: vertical sync lines: + // "(0 means 16 on some CRTC. Not present on all CRTCs, fixed to 16 lines on these)" + break; + case 4: layout_.vertical.total = value & 0x7f; break; + case 5: layout_.vertical.adjust = value & 0x1f; break; + case 6: layout_.vertical.displayed = value & 0x7f; break; + case 7: layout_.vertical.start_sync = value & 0x7f; break; + case 8: + switch(value & 3) { + default: layout_.interlace_mode_ = InterlaceMode::Off; break; + case 0b01: layout_.interlace_mode_ = InterlaceMode::InterlaceSync; break; + case 0b11: layout_.interlace_mode_ = InterlaceMode::InterlaceSyncAndVideo; break; + } + + // Per CPC documentation, skew doesn't work on a "type 1 or 2", i.e. an MC6845 or a UM6845R. + if(personality != Personality::UM6845R && personality != Personality::MC6845) { + switch((value >> 4)&3) { + default: display_skew_mask_ = 1; break; + case 1: display_skew_mask_ = 2; break; + case 2: display_skew_mask_ = 4; break; + } + } + break; + case 9: layout_.vertical.end_row = value & 0x1f; break; + case 10: + layout_.vertical.start_cursor = value & 0x1f; + layout_.cursor_flags = (value >> 5) & 3; + break; + case 11: + layout_.vertical.end_cursor = value & 0x1f; + break; + case 12: load_high(layout_.start_address); break; + case 13: load_low(layout_.start_address); break; + case 14: load_high(layout_.cursor_address); break; + case 15: load_low(layout_.cursor_address); break; + } + + static constexpr uint8_t masks[] = { + 0xff, // Horizontal total. + 0xff, // Horizontal display end. + 0xff, // Start horizontal blank. + 0xff, // + // EGA: b0–b4: end of horizontal blank; + // b5–b6: "Number of character clocks to delay start of display after Horizontal Total has been reached." + + is_ega ? 0xff : 0x7f, // Start horizontal retrace. + 0x1f, 0x7f, 0x7f, + 0xff, 0x1f, 0x7f, 0x1f, + uint8_t(RefreshMask >> 8), uint8_t(RefreshMask), + uint8_t(RefreshMask >> 8), uint8_t(RefreshMask), + }; + + if(selected_register_ < 16) { + registers_[selected_register_] = value & masks[selected_register_]; + } + if(selected_register_ == 31 && personality == Personality::UM6845R) { + dummy_register_ = value; + } + } + + void trigger_light_pen() { + registers_[17] = bus_state_.refresh_address & 0xff; + registers_[16] = bus_state_.refresh_address >> 8; + status_ |= 0x40; + } + + void run_for(const Cycles cycles) { + auto cyles_remaining = cycles.as_integral(); + while(cyles_remaining--) { + // Intention of code below: all conditionals are evaluated as if functional; they should be + // ordered so that whatever assignments result don't affect any subsequent conditionals + + + // Do bus work. + bus_state_.cursor = is_cursor_line_ && + bus_state_.refresh_address == layout_.cursor_address; + bus_state_.display_enable = character_is_visible_ && line_is_visible_; + bus_handler_.perform_bus_cycle(bus_state_); + + // + // Shared, stateless signals. + // + const bool character_total_hit = character_counter_ == layout_.horizontal.total; + const uint8_t lines_per_row = + layout_.interlace_mode_ == InterlaceMode::InterlaceSyncAndVideo ? + layout_.vertical.end_row & ~1 : layout_.vertical.end_row; + const bool row_end_hit = bus_state_.row_address == lines_per_row && !is_in_adjustment_period_; + const bool was_eof = eof_latched_; + const bool new_frame = + character_total_hit && was_eof && + ( + layout_.interlace_mode_ == InterlaceMode::Off || + !odd_field_ + ); + + // + // Horizontal. + // + + // Update horizontal sync. + if(bus_state_.hsync) { + ++hsync_counter_; + bus_state_.hsync = hsync_counter_ != layout_.horizontal.sync_width; + } + if(character_counter_ == layout_.horizontal.start_sync) { + hsync_counter_ = 0; + bus_state_.hsync = true; + } + + // Check for end-of-line. + character_reset_history_ <<= 1; + if(character_total_hit) { + character_counter_ = 0; + character_is_visible_ = true; + character_reset_history_ |= 1; + } else { + character_counter_++; + } + + // Check for end of visible characters. + if(character_counter_ == layout_.horizontal.displayed) { + character_is_visible_ = false; + } + + // + // End-of-frame. + // + + if(character_total_hit) { + if(was_eof) { + eof_latched_ = eom_latched_ = is_in_adjustment_period_ = false; + adjustment_counter_ = 0; + } else if(is_in_adjustment_period_) { + adjustment_counter_ = (adjustment_counter_ + 1) & 31; + } + } + + if(character_reset_history_ & 2) { + eom_latched_ |= row_end_hit && row_counter_ == layout_.vertical.total; + } + + if(character_reset_history_ & 4 && eom_latched_) { + // TODO: I don't believe the "add 1 for interlaced" test here is accurate; + // others represent the extra scanline as additional state, presumably because + // adjust total might be reprogrammed at any time. + const auto adjust_length = + layout_.vertical.adjust + (layout_.interlace_mode_ != InterlaceMode::Off && odd_field_ ? 1 : 0); + is_in_adjustment_period_ |= adjustment_counter_ != adjust_length; + eof_latched_ |= adjustment_counter_ == adjust_length; + } + + // + // Vertical. + // + + // Sync. + const bool vsync_horizontal = + (!odd_field_ && !character_counter_) || + (odd_field_ && character_counter_ == (layout_.horizontal.total >> 1)); + if(vsync_horizontal) { + if((row_counter_ == layout_.vertical.start_sync && !bus_state_.row_address) || bus_state_.vsync) { + bus_state_.vsync = true; + vsync_counter_ = (vsync_counter_ + 1) & 0xf; + } else { + vsync_counter_ = 0; + } + + if(vsync_counter_ == layout_.vertical.sync_lines) { + bus_state_.vsync = false; + } + } + + // Row address. + if(character_total_hit) { + if(was_eof) { + bus_state_.row_address = 0; + eof_latched_ = eom_latched_ = false; + } else if(row_end_hit) { + bus_state_.row_address = 0; + } else if(layout_.interlace_mode_ == InterlaceMode::InterlaceSyncAndVideo) { + bus_state_.row_address = (bus_state_.row_address + 2) & ~1 & 31; + } else { + bus_state_.row_address = (bus_state_.row_address + 1) & 31; + } + } + + // Row counter. + row_counter_ = next_row_counter_; + if(new_frame) { + next_row_counter_ = 0; + is_first_scanline_ = true; + } else { + next_row_counter_ = row_end_hit && character_total_hit ? + (next_row_counter_ + 1) : next_row_counter_; + is_first_scanline_ &= !row_end_hit; + } + + // Vertical display enable. + if(is_first_scanline_) { + line_is_visible_ = true; + odd_field_ = bus_state_.field_count & 1; + } else if(line_is_visible_ && row_counter_ == layout_.vertical.displayed) { + line_is_visible_ = false; + ++bus_state_.field_count; + } + + + // Cursor. + if constexpr (cursor_type != CursorType::None) { + // Check for cursor enable. + is_cursor_line_ |= bus_state_.row_address == layout_.vertical.start_cursor; + is_cursor_line_ &= bus_state_.row_address != layout_.vertical.end_cursor; + + switch(cursor_type) { + // MDA-style blinking. + // https://retrocomputing.stackexchange.com/questions/27803/what-are-the-blinking-rates-of-the-caret-and-of-blinking-text-on-pc-graphics-car + // gives an 8/8 pattern for regular blinking though mode 11 is then just a guess. + case CursorType::MDA: + switch(layout_.cursor_flags) { + case 0b11: is_cursor_line_ &= (bus_state_.field_count & 8) < 3; break; + case 0b00: is_cursor_line_ &= bool(bus_state_.field_count & 8); break; + case 0b01: is_cursor_line_ = false; break; + case 0b10: is_cursor_line_ = true; break; + default: break; + } + break; + } + } + + // + // Addressing. + // + + if(new_frame) { + bus_state_.refresh_address = layout_.start_address; + } else if(character_total_hit) { + bus_state_.refresh_address = line_address_; + } else { + bus_state_.refresh_address = (bus_state_.refresh_address + 1) & RefreshMask; + } + + if(new_frame) { + line_address_ = layout_.start_address; + } else if(character_counter_ == layout_.horizontal.displayed && row_end_hit) { + line_address_ = bus_state_.refresh_address; + } + } + } + + const BusState &get_bus_state() const { + return bus_state_; + } + +private: + static constexpr uint16_t RefreshMask = (personality >= Personality::EGA) ? 0xffff : 0x3fff; + + BusHandlerT &bus_handler_; + BusState bus_state_; + + enum class InterlaceMode { + Off, + InterlaceSync, + InterlaceSyncAndVideo, + }; + enum class BlinkMode { + // TODO. + }; + struct { struct { - struct { - uint8_t total; - uint8_t displayed; - uint8_t start_sync; - uint8_t sync_width; - } horizontal; + uint8_t total; + uint8_t displayed; + uint8_t start_sync; + uint8_t sync_width; + } horizontal; - struct { - uint8_t total; - uint8_t displayed; - uint8_t start_sync; - uint8_t sync_lines; - uint8_t adjust; + struct { + uint8_t total; + uint8_t displayed; + uint8_t start_sync; + uint8_t sync_lines; + uint8_t adjust; - uint8_t end_row; - uint8_t start_cursor; - uint8_t end_cursor; - } vertical; + uint8_t end_row; + uint8_t start_cursor; + uint8_t end_cursor; + } vertical; - InterlaceMode interlace_mode_ = InterlaceMode::Off; - uint8_t end_row() const { - return interlace_mode_ == InterlaceMode::InterlaceSyncAndVideo ? vertical.end_row & ~1 : vertical.end_row; - } + InterlaceMode interlace_mode_ = InterlaceMode::Off; + uint8_t end_row() const { + return interlace_mode_ == InterlaceMode::InterlaceSyncAndVideo ? vertical.end_row & ~1 : vertical.end_row; + } - uint16_t start_address; - uint16_t cursor_address; - uint16_t light_pen_address; - uint8_t cursor_flags; - } layout_; + uint16_t start_address; + uint16_t cursor_address; + uint16_t light_pen_address; + uint8_t cursor_flags; + } layout_; - uint8_t registers_[18]{}; - uint8_t dummy_register_ = 0; - int selected_register_ = 0; + uint8_t registers_[18]{}; + uint8_t dummy_register_ = 0; + int selected_register_ = 0; - uint8_t character_counter_ = 0; - uint32_t character_reset_history_ = 0; - uint8_t row_counter_ = 0, next_row_counter_ = 0; - uint8_t adjustment_counter_ = 0; + uint8_t character_counter_ = 0; + uint32_t character_reset_history_ = 0; + uint8_t row_counter_ = 0, next_row_counter_ = 0; + uint8_t adjustment_counter_ = 0; - bool character_is_visible_ = false; - bool line_is_visible_ = false; - bool is_first_scanline_ = false; - bool is_cursor_line_ = false; + bool character_is_visible_ = false; + bool line_is_visible_ = false; + bool is_first_scanline_ = false; + bool is_cursor_line_ = false; - int hsync_counter_ = 0; - int vsync_counter_ = 0; - bool is_in_adjustment_period_ = false; + int hsync_counter_ = 0; + int vsync_counter_ = 0; + bool is_in_adjustment_period_ = false; - uint16_t line_address_ = 0; - uint8_t status_ = 0; + uint16_t line_address_ = 0; + uint8_t status_ = 0; - int display_skew_mask_ = 1; - unsigned int character_is_visible_shifter_ = 0; + int display_skew_mask_ = 1; + unsigned int character_is_visible_shifter_ = 0; - bool eof_latched_ = false; - bool eom_latched_ = false; - uint16_t next_row_address_ = 0; - bool odd_field_ = false; + bool eof_latched_ = false; + bool eom_latched_ = false; + uint16_t next_row_address_ = 0; + bool odd_field_ = false; }; } diff --git a/Components/6850/6850.cpp b/Components/6850/6850.cpp index 032b02887..21d07c19e 100644 --- a/Components/6850/6850.cpp +++ b/Components/6850/6850.cpp @@ -21,7 +21,7 @@ ACIA::ACIA(HalfCycles transmit_clock_rate, HalfCycles receive_clock_rate) : request_to_send.set_writer_clock_rate(transmit_clock_rate); } -uint8_t ACIA::read(int address) { +uint8_t ACIA::read(const int address) { if(address&1) { overran_ = false; received_data_ |= NoValueMask; @@ -46,7 +46,7 @@ void ACIA::reset() { assert(!interrupt_line_); } -void ACIA::write(int address, uint8_t value) { +void ACIA::write(const int address, const uint8_t value) { if(address&1) { next_transmission_ = value; consider_transmission(); @@ -148,7 +148,7 @@ uint8_t ACIA::parity(uint8_t value) { return value ^ (parity_ == Parity::Even); } -bool ACIA::serial_line_did_produce_bit(Serial::Line *, int bit) { +bool ACIA::serial_line_did_produce_bit(Serial::Line *, const int bit) { // Shift this bit into the 11-bit input register; this is big enough to hold // the largest transmission symbol. ++bits_received_; @@ -172,7 +172,7 @@ bool ACIA::serial_line_did_produce_bit(Serial::Line *, int bit) { return true; } -void ACIA::set_interrupt_delegate(InterruptDelegate *delegate) { +void ACIA::set_interrupt_delegate(InterruptDelegate *const delegate) { interrupt_delegate_ = delegate; } diff --git a/Components/6850/6850.hpp b/Components/6850/6850.hpp index 88fe92996..b350f0956 100644 --- a/Components/6850/6850.hpp +++ b/Components/6850/6850.hpp @@ -17,111 +17,111 @@ namespace Motorola::ACIA { class ACIA: public ClockingHint::Source, private Serial::Line::ReadDelegate { - public: - static constexpr const HalfCycles SameAsTransmit = HalfCycles(0); +public: + static constexpr const HalfCycles SameAsTransmit = HalfCycles(0); - /*! - Constructs a new instance of ACIA which will receive a transmission clock at a rate of - @c transmit_clock_rate, and a receive clock at a rate of @c receive_clock_rate. - */ - ACIA(HalfCycles transmit_clock_rate, HalfCycles receive_clock_rate = SameAsTransmit); + /*! + Constructs a new instance of ACIA which will receive a transmission clock at a rate of + @c transmit_clock_rate, and a receive clock at a rate of @c receive_clock_rate. + */ + ACIA(HalfCycles transmit_clock_rate, HalfCycles receive_clock_rate = SameAsTransmit); - /*! - Reads from the ACIA. + /*! + Reads from the ACIA. - Bit 0 of the address is used as the ACIA's register select line — - so even addresses select control/status registers, odd addresses - select transmit/receive data registers. - */ - uint8_t read(int address); + Bit 0 of the address is used as the ACIA's register select line — + so even addresses select control/status registers, odd addresses + select transmit/receive data registers. + */ + uint8_t read(int address); - /*! - Writes to the ACIA. + /*! + Writes to the ACIA. - Bit 0 of the address is used as the ACIA's register select line — - so even addresses select control/status registers, odd addresses - select transmit/receive data registers. - */ - void write(int address, uint8_t value); + Bit 0 of the address is used as the ACIA's register select line — + so even addresses select control/status registers, odd addresses + select transmit/receive data registers. + */ + void write(int address, uint8_t value); - /*! - Advances @c transmission_cycles in time, which should be - counted relative to the @c transmit_clock_rate. - */ - forceinline void run_for(HalfCycles transmission_cycles) { - if(transmit.transmission_data_time_remaining() > HalfCycles(0)) { - const auto write_data_time_remaining = transmit.write_data_time_remaining(); + /*! + Advances @c transmission_cycles in time, which should be + counted relative to the @c transmit_clock_rate. + */ + forceinline void run_for(const HalfCycles transmission_cycles) { + if(transmit.transmission_data_time_remaining() > HalfCycles(0)) { + const auto write_data_time_remaining = transmit.write_data_time_remaining(); - // There's at most one further byte available to enqueue, so a single 'if' - // rather than a 'while' is correct here. It's the responsibilit of the caller - // to ensure run_for lengths are appropriate for longer sequences. - if(transmission_cycles >= write_data_time_remaining) { - if(next_transmission_ != NoValueMask) { - transmit.advance_writer(write_data_time_remaining); - consider_transmission(); - transmit.advance_writer(transmission_cycles - write_data_time_remaining); - } else { - transmit.advance_writer(transmission_cycles); - update_clocking_observer(); - update_interrupt_line(); - } + // There's at most one further byte available to enqueue, so a single 'if' + // rather than a 'while' is correct here. It's the responsibilit of the caller + // to ensure run_for lengths are appropriate for longer sequences. + if(transmission_cycles >= write_data_time_remaining) { + if(next_transmission_ != NoValueMask) { + transmit.advance_writer(write_data_time_remaining); + consider_transmission(); + transmit.advance_writer(transmission_cycles - write_data_time_remaining); } else { transmit.advance_writer(transmission_cycles); + update_clocking_observer(); + update_interrupt_line(); } + } else { + transmit.advance_writer(transmission_cycles); } } + } - bool get_interrupt_line() const; - void reset(); + bool get_interrupt_line() const; + void reset(); - // Input lines. - Serial::Line receive; - Serial::Line clear_to_send; - Serial::Line data_carrier_detect; + // Input lines. + Serial::Line receive; + Serial::Line clear_to_send; + Serial::Line data_carrier_detect; - // Output lines. - Serial::Line transmit; - Serial::Line request_to_send; + // Output lines. + Serial::Line transmit; + Serial::Line request_to_send; - // ClockingHint::Source. - ClockingHint::Preference preferred_clocking() const final; + // ClockingHint::Source. + ClockingHint::Preference preferred_clocking() const final; - struct InterruptDelegate { - virtual void acia6850_did_change_interrupt_status(ACIA *acia) = 0; - }; - void set_interrupt_delegate(InterruptDelegate *delegate); + struct InterruptDelegate { + virtual void acia6850_did_change_interrupt_status(ACIA *acia) = 0; + }; + void set_interrupt_delegate(InterruptDelegate *delegate); - private: - int divider_ = 1; - enum class Parity { - Even, Odd, None - } parity_ = Parity::None; - int data_bits_ = 7, stop_bits_ = 2; +private: + int divider_ = 1; + enum class Parity { + Even, Odd, None + } parity_ = Parity::None; + int data_bits_ = 7, stop_bits_ = 2; - static constexpr int NoValueMask = 0x100; - int next_transmission_ = NoValueMask; - int received_data_ = NoValueMask; + static constexpr int NoValueMask = 0x100; + int next_transmission_ = NoValueMask; + int received_data_ = NoValueMask; - int bits_received_ = 0; - int bits_incoming_ = 0; - bool overran_ = false; + int bits_received_ = 0; + int bits_incoming_ = 0; + bool overran_ = false; - void consider_transmission(); - int expected_bits(); - uint8_t parity(uint8_t value); + void consider_transmission(); + int expected_bits(); + uint8_t parity(uint8_t value); - bool receive_interrupt_enabled_ = false; - bool transmit_interrupt_enabled_ = false; + bool receive_interrupt_enabled_ = false; + bool transmit_interrupt_enabled_ = false; - HalfCycles transmit_clock_rate_; - HalfCycles receive_clock_rate_; + HalfCycles transmit_clock_rate_; + HalfCycles receive_clock_rate_; - bool serial_line_did_produce_bit(Serial::Line *line, int bit) final; + bool serial_line_did_produce_bit(Serial::Line *line, int bit) final; - bool interrupt_line_ = false; - void update_interrupt_line(); - InterruptDelegate *interrupt_delegate_ = nullptr; - uint8_t get_status(); + bool interrupt_line_ = false; + void update_interrupt_line(); + InterruptDelegate *interrupt_delegate_ = nullptr; + uint8_t get_status(); }; } diff --git a/Components/8255/i8255.hpp b/Components/8255/i8255.hpp index 8ea42f67b..1ea3dc8f3 100644 --- a/Components/8255/i8255.hpp +++ b/Components/8255/i8255.hpp @@ -13,77 +13,80 @@ namespace Intel::i8255 { class PortHandler { - public: - void set_value([[maybe_unused]] int port, [[maybe_unused]] uint8_t value) {} - uint8_t get_value([[maybe_unused]] int port) { return 0xff; } +public: + void set_value([[maybe_unused]] int port, [[maybe_unused]] uint8_t value) {} + uint8_t get_value([[maybe_unused]] int port) { return 0xff; } }; // TODO: Modes 1 and 2. template class i8255 { - public: - i8255(T &port_handler) : control_(0), outputs_{0, 0, 0}, port_handler_(port_handler) {} +public: + i8255(T &port_handler) : control_(0), outputs_{0, 0, 0}, port_handler_(port_handler) {} - /*! - Stores the value @c value to the register at @c address. If this causes a change in 8255 output - then the PortHandler will be informed. - */ - void write(int address, uint8_t value) { - switch(address & 3) { - case 0: - if(!(control_ & 0x10)) { - // TODO: so what would output be when switching from input to output mode? - outputs_[0] = value; port_handler_.set_value(0, value); - } - break; - case 1: - if(!(control_ & 0x02)) { - outputs_[1] = value; port_handler_.set_value(1, value); - } - break; - case 2: outputs_[2] = value; port_handler_.set_value(2, value); break; - case 3: - if(value & 0x80) { - control_ = value; - } else { - if(value & 1) { - outputs_[2] |= 1 << ((value >> 1)&7); - } else { - outputs_[2] &= ~(1 << ((value >> 1)&7)); - } - } - update_outputs(); - break; - } - } - - /*! - Obtains the current value for the register at @c address. If this provides a reading - of input then the PortHandler will be queried. - */ - uint8_t read(int address) { - switch(address & 3) { - case 0: return (control_ & 0x10) ? port_handler_.get_value(0) : outputs_[0]; - case 1: return (control_ & 0x02) ? port_handler_.get_value(1) : outputs_[1]; - case 2: { - if(!(control_ & 0x09)) return outputs_[2]; - uint8_t input = port_handler_.get_value(2); - return ((control_ & 0x01) ? (input & 0x0f) : (outputs_[2] & 0x0f)) | ((control_ & 0x08) ? (input & 0xf0) : (outputs_[2] & 0xf0)); + /*! + Stores the value @c value to the register at @c address. If this causes a change in 8255 output + then the PortHandler will be informed. + */ + void write(const int address, const uint8_t value) { + switch(address & 3) { + case 0: + if(!(control_ & 0x10)) { + // TODO: so what would output be when switching from input to output mode? + outputs_[0] = value; port_handler_.set_value(0, value); } - case 3: return control_; + break; + case 1: + if(!(control_ & 0x02)) { + outputs_[1] = value; port_handler_.set_value(1, value); + } + break; + case 2: outputs_[2] = value; port_handler_.set_value(2, value); break; + case 3: + if(value & 0x80) { + control_ = value; + } else { + if(value & 1) { + outputs_[2] |= 1 << ((value >> 1)&7); + } else { + outputs_[2] &= ~(1 << ((value >> 1)&7)); + } + } + update_outputs(); + break; + } + } + + /*! + Obtains the current value for the register at @c address. If this provides a reading + of input then the PortHandler will be queried. + */ + uint8_t read(const int address) { + switch(address & 3) { + case 0: return (control_ & 0x10) ? port_handler_.get_value(0) : outputs_[0]; + case 1: return (control_ & 0x02) ? port_handler_.get_value(1) : outputs_[1]; + case 2: { + if(!(control_ & 0x09)) return outputs_[2]; + uint8_t input = port_handler_.get_value(2); + return ((control_ & 0x01) ? + (input & 0x0f) : + (outputs_[2] & 0x0f)) | ((control_ & 0x08) ? + (input & 0xf0) : (outputs_[2] & 0xf0)); } - return 0xff; + case 3: return control_; } + return 0xff; + } - private: - void update_outputs() { - if(!(control_ & 0x10)) port_handler_.set_value(0, outputs_[0]); - if(!(control_ & 0x02)) port_handler_.set_value(1, outputs_[1]); - port_handler_.set_value(2, outputs_[2]); - } +private: + void update_outputs() { + if(!(control_ & 0x10)) port_handler_.set_value(0, outputs_[0]); + if(!(control_ & 0x02)) port_handler_.set_value(1, outputs_[1]); + port_handler_.set_value(2, outputs_[2]); + } - uint8_t control_; - uint8_t outputs_[3]; - T &port_handler_; + uint8_t control_; + uint8_t outputs_[3]; + T &port_handler_; }; } diff --git a/Components/8272/CommandDecoder.hpp b/Components/8272/CommandDecoder.hpp index e0386c2aa..5d36b1ef4 100644 --- a/Components/8272/CommandDecoder.hpp +++ b/Components/8272/CommandDecoder.hpp @@ -40,179 +40,179 @@ enum class Command { }; class CommandDecoder { - public: - /// Add a byte to the current command. - void push_back(uint8_t byte) { - command_.push_back(byte); +public: + /// Add a byte to the current command. + void push_back(uint8_t byte) { + command_.push_back(byte); + } + + /// Reset decoding. + void clear() { + command_.clear(); + } + + /// @returns @c true if an entire command has been received; @c false if further bytes are needed. + bool has_command() const { + if(!command_.size()) { + return false; } - /// Reset decoding. - void clear() { - command_.clear(); - } + static constexpr std::size_t required_lengths[32] = { + 0, 0, 9, 3, 2, 9, 9, 2, + 1, 9, 2, 0, 9, 6, 0, 3, + 0, 9, 0, 0, 0, 0, 0, 0, + 0, 9, 0, 0, 0, 9, 0, 0, + }; - /// @returns @c true if an entire command has been received; @c false if further bytes are needed. - bool has_command() const { - if(!command_.size()) { + return command_.size() >= required_lengths[command_[0] & 0x1f]; + } + + /// @returns The command requested. Valid only if @c has_command() is @c true. + Command command() const { + const auto command = Command(command_[0] & 0x1f); + + switch(command) { + case Command::ReadData: case Command::ReadDeletedData: + case Command::WriteData: case Command::WriteDeletedData: + case Command::ReadTrack: case Command::ReadID: + case Command::FormatTrack: + case Command::ScanLow: case Command::ScanLowOrEqual: + case Command::ScanHighOrEqual: + case Command::Recalibrate: case Command::Seek: + case Command::SenseInterruptStatus: + case Command::Specify: case Command::SenseDriveStatus: + return command; + + default: return Command::Invalid; + } + } + + // + // Commands that specify geometry; i.e. + // + // * ReadData; + // * ReadDeletedData; + // * WriteData; + // * WriteDeletedData; + // * ReadTrack; + // * ScanEqual; + // * ScanLowOrEqual; + // * ScanHighOrEqual. + // + + /// @returns @c true if this command specifies geometry, in which case geomtry() is well-defined. + /// @c false otherwise. + bool has_geometry() const { return command_.size() == 9; } + struct Geometry { + uint8_t cylinder, head, sector, size, end_of_track; + }; + Geometry geometry() const { + Geometry result; + result.cylinder = command_[2]; + result.head = command_[3]; + result.sector = command_[4]; + result.size = command_[5]; + result.end_of_track = command_[6]; + return result; + } + + // + // Commands that imply data access; i.e. + // + // * ReadData; + // * ReadDeletedData; + // * WriteData; + // * WriteDeletedData; + // * ReadTrack; + // * ReadID; + // * FormatTrack; + // * ScanLow; + // * ScanLowOrEqual; + // * ScanHighOrEqual. + // + + /// @returns @c true if this command involves reading or writing data, in which case target() will be valid. + /// @c false otherwise. + bool is_access() const { + switch(command()) { + case Command::ReadData: case Command::ReadDeletedData: + case Command::WriteData: case Command::WriteDeletedData: + case Command::ReadTrack: case Command::ReadID: + case Command::FormatTrack: + case Command::ScanLow: case Command::ScanLowOrEqual: + case Command::ScanHighOrEqual: + return true; + + default: return false; - } - - static constexpr std::size_t required_lengths[32] = { - 0, 0, 9, 3, 2, 9, 9, 2, - 1, 9, 2, 0, 9, 6, 0, 3, - 0, 9, 0, 0, 0, 0, 0, 0, - 0, 9, 0, 0, 0, 9, 0, 0, - }; - - return command_.size() >= required_lengths[command_[0] & 0x1f]; } + } + struct AccessTarget { + uint8_t drive, head; + bool mfm, skip_deleted; + }; + AccessTarget target() const { + AccessTarget result; + result.drive = command_[1] & 0x03; + result.head = (command_[1] >> 2) & 0x01; + result.mfm = command_[0] & 0x40; + result.skip_deleted = command_[0] & 0x20; + return result; + } + uint8_t drive_head() const { + return command_[1] & 7; + } - /// @returns The command requested. Valid only if @c has_command() is @c true. - Command command() const { - const auto command = Command(command_[0] & 0x1f); + // + // Command::FormatTrack + // - switch(command) { - case Command::ReadData: case Command::ReadDeletedData: - case Command::WriteData: case Command::WriteDeletedData: - case Command::ReadTrack: case Command::ReadID: - case Command::FormatTrack: - case Command::ScanLow: case Command::ScanLowOrEqual: - case Command::ScanHighOrEqual: - case Command::Recalibrate: case Command::Seek: - case Command::SenseInterruptStatus: - case Command::Specify: case Command::SenseDriveStatus: - return command; + struct FormatSpecs { + uint8_t bytes_per_sector; + uint8_t sectors_per_track; + uint8_t gap3_length; + uint8_t filler; + }; + FormatSpecs format_specs() const { + FormatSpecs result; + result.bytes_per_sector = command_[2]; + result.sectors_per_track = command_[3]; + result.gap3_length = command_[4]; + result.filler = command_[5]; + return result; + } - default: return Command::Invalid; - } - } + // + // Command::Seek + // - // - // Commands that specify geometry; i.e. - // - // * ReadData; - // * ReadDeletedData; - // * WriteData; - // * WriteDeletedData; - // * ReadTrack; - // * ScanEqual; - // * ScanLowOrEqual; - // * ScanHighOrEqual. - // + /// @returns The desired target track. + uint8_t seek_target() const { + return command_[2]; + } - /// @returns @c true if this command specifies geometry, in which case geomtry() is well-defined. - /// @c false otherwise. - bool has_geometry() const { return command_.size() == 9; } - struct Geometry { - uint8_t cylinder, head, sector, size, end_of_track; - }; - Geometry geometry() const { - Geometry result; - result.cylinder = command_[2]; - result.head = command_[3]; - result.sector = command_[4]; - result.size = command_[5]; - result.end_of_track = command_[6]; - return result; - } + // + // Command::Specify + // - // - // Commands that imply data access; i.e. - // - // * ReadData; - // * ReadDeletedData; - // * WriteData; - // * WriteDeletedData; - // * ReadTrack; - // * ReadID; - // * FormatTrack; - // * ScanLow; - // * ScanLowOrEqual; - // * ScanHighOrEqual. - // + struct SpecifySpecs { + // The below are all in milliseconds. + uint8_t step_rate_time; + uint8_t head_unload_time; + uint8_t head_load_time; + bool use_dma; + }; + SpecifySpecs specify_specs() const { + SpecifySpecs result; + result.step_rate_time = 16 - (command_[1] >> 4); // i.e. 1 to 16ms + result.head_unload_time = uint8_t((command_[1] & 0x0f) << 4); // i.e. 16 to 240ms + result.head_load_time = command_[2] & ~1; // i.e. 2 to 254 ms in increments of 2ms + result.use_dma = !(command_[2] & 1); + return result; + } - /// @returns @c true if this command involves reading or writing data, in which case target() will be valid. - /// @c false otherwise. - bool is_access() const { - switch(command()) { - case Command::ReadData: case Command::ReadDeletedData: - case Command::WriteData: case Command::WriteDeletedData: - case Command::ReadTrack: case Command::ReadID: - case Command::FormatTrack: - case Command::ScanLow: case Command::ScanLowOrEqual: - case Command::ScanHighOrEqual: - return true; - - default: - return false; - } - } - struct AccessTarget { - uint8_t drive, head; - bool mfm, skip_deleted; - }; - AccessTarget target() const { - AccessTarget result; - result.drive = command_[1] & 0x03; - result.head = (command_[1] >> 2) & 0x01; - result.mfm = command_[0] & 0x40; - result.skip_deleted = command_[0] & 0x20; - return result; - } - uint8_t drive_head() const { - return command_[1] & 7; - } - - // - // Command::FormatTrack - // - - struct FormatSpecs { - uint8_t bytes_per_sector; - uint8_t sectors_per_track; - uint8_t gap3_length; - uint8_t filler; - }; - FormatSpecs format_specs() const { - FormatSpecs result; - result.bytes_per_sector = command_[2]; - result.sectors_per_track = command_[3]; - result.gap3_length = command_[4]; - result.filler = command_[5]; - return result; - } - - // - // Command::Seek - // - - /// @returns The desired target track. - uint8_t seek_target() const { - return command_[2]; - } - - // - // Command::Specify - // - - struct SpecifySpecs { - // The below are all in milliseconds. - uint8_t step_rate_time; - uint8_t head_unload_time; - uint8_t head_load_time; - bool use_dma; - }; - SpecifySpecs specify_specs() const { - SpecifySpecs result; - result.step_rate_time = 16 - (command_[1] >> 4); // i.e. 1 to 16ms - result.head_unload_time = uint8_t((command_[1] & 0x0f) << 4); // i.e. 16 to 240ms - result.head_load_time = command_[2] & ~1; // i.e. 2 to 254 ms in increments of 2ms - result.use_dma = !(command_[2] & 1); - return result; - } - - private: - std::vector command_; +private: + std::vector command_; }; } diff --git a/Components/8272/Results.hpp b/Components/8272/Results.hpp index 13297afee..08477cd26 100644 --- a/Components/8272/Results.hpp +++ b/Components/8272/Results.hpp @@ -14,49 +14,55 @@ namespace Intel::i8272 { class Results { - public: - /// Serialises the response to Command::Invalid and Command::SenseInterruptStatus when no interrupt source was found. - void serialise_none() { - result_ = { 0x80 }; - } +public: + /// Serialises the response to Command::Invalid and Command::SenseInterruptStatus when no interrupt source was found. + void serialise_none() { + result_ = { 0x80 }; + } - /// Serialises the response to Command::SenseInterruptStatus for a found drive. - void serialise(const Status &status, uint8_t cylinder) { - result_ = { cylinder, status[0] }; - } + /// Serialises the response to Command::SenseInterruptStatus for a found drive. + void serialise(const Status &status, const uint8_t cylinder) { + result_ = { cylinder, status[0] }; + } - /// Serialises the seven-byte response to Command::SenseDriveStatus. - void serialise(uint8_t flags, uint8_t drive_side) { - result_ = { uint8_t(flags | drive_side) }; - } + /// Serialises the seven-byte response to Command::SenseDriveStatus. + void serialise(const uint8_t flags, const uint8_t drive_side) { + result_ = { uint8_t(flags | drive_side) }; + } - /// Serialises the response to: - /// - /// * Command::ReadData; - /// * Command::ReadDeletedData; - /// * Command::WriteData; - /// * Command::WriteDeletedData; - /// * Command::ReadID; - /// * Command::ReadTrack; - /// * Command::FormatTrack; - /// * Command::ScanLow; and - /// * Command::ScanHighOrEqual. - void serialise(const Status &status, uint8_t cylinder, uint8_t head, uint8_t sector, uint8_t size) { - result_ = { size, sector, head, cylinder, status[2], status[1], status[0] }; - } + /// Serialises the response to: + /// + /// * Command::ReadData; + /// * Command::ReadDeletedData; + /// * Command::WriteData; + /// * Command::WriteDeletedData; + /// * Command::ReadID; + /// * Command::ReadTrack; + /// * Command::FormatTrack; + /// * Command::ScanLow; and + /// * Command::ScanHighOrEqual. + void serialise( + const Status &status, + const uint8_t cylinder, + const uint8_t head, + const uint8_t sector, + const uint8_t size + ) { + result_ = { size, sector, head, cylinder, status[2], status[1], status[0] }; + } - /// @returns @c true if all result bytes are exhausted; @c false otherwise. - bool empty() const { return result_.empty(); } + /// @returns @c true if all result bytes are exhausted; @c false otherwise. + bool empty() const { return result_.empty(); } - /// @returns The next byte of the result. - uint8_t next() { - const uint8_t next = result_.back(); - result_.pop_back(); - return next; - } + /// @returns The next byte of the result. + uint8_t next() { + const uint8_t next = result_.back(); + result_.pop_back(); + return next; + } - private: - std::vector result_; +private: + std::vector result_; }; } diff --git a/Components/8272/Status.hpp b/Components/8272/Status.hpp index 2b3f63be4..04990092e 100644 --- a/Components/8272/Status.hpp +++ b/Components/8272/Status.hpp @@ -66,66 +66,66 @@ enum class Status3: uint8_t { }; class Status { - public: - Status() { - reset(); +public: + Status() { + reset(); + } + + void reset() { + main_status_ = 0; + set(MainStatus::DataReady, true); + status_[0] = status_[1] = status_[2] = 0; + } + + /// @returns The main status register value. + uint8_t main() const { + return main_status_; + } + uint8_t operator [](const int index) const { + return status_[index]; + } + + // + // Flag setters. + // + void set(const MainStatus flag, const bool value) { + set(uint8_t(flag), value, main_status_); + } + void start_seek(const int drive) { main_status_ |= 1 << drive; } + void set(const Status0 flag) { set(uint8_t(flag), true, status_[0]); } + void set(const Status1 flag) { set(uint8_t(flag), true, status_[1]); } + void set(const Status2 flag) { set(uint8_t(flag), true, status_[2]); } + + void set_status0(uint8_t value) { status_[0] = value; } + + // + // Flag getters. + // + bool get(const MainStatus flag) { return main_status_ & uint8_t(flag); } + bool get(const Status2 flag) { return status_[2] & uint8_t(flag); } + + /// Begin execution of whatever @c CommandDecoder currently describes, setting internal + /// state appropriately. + void begin(const CommandDecoder &command) { + set(MainStatus::DataReady, false); + set(MainStatus::CommandInProgress, true); + + if(command.is_access()) { + status_[0] = command.drive_head(); } + } - void reset() { - main_status_ = 0; - set(MainStatus::DataReady, true); - status_[0] = status_[1] = status_[2] = 0; +private: + void set(const uint8_t flag, const bool value, uint8_t &target) { + if(value) { + target |= flag; + } else { + target &= ~flag; } + } - /// @returns The main status register value. - uint8_t main() const { - return main_status_; - } - uint8_t operator [](int index) const { - return status_[index]; - } - - // - // Flag setters. - // - void set(MainStatus flag, bool value) { - set(uint8_t(flag), value, main_status_); - } - void start_seek(int drive) { main_status_ |= 1 << drive; } - void set(Status0 flag) { set(uint8_t(flag), true, status_[0]); } - void set(Status1 flag) { set(uint8_t(flag), true, status_[1]); } - void set(Status2 flag) { set(uint8_t(flag), true, status_[2]); } - - void set_status0(uint8_t value) { status_[0] = value; } - - // - // Flag getters. - // - bool get(MainStatus flag) { return main_status_ & uint8_t(flag); } - bool get(Status2 flag) { return status_[2] & uint8_t(flag); } - - /// Begin execution of whatever @c CommandDecoder currently describes, setting internal - /// state appropriately. - void begin(const CommandDecoder &command) { - set(MainStatus::DataReady, false); - set(MainStatus::CommandInProgress, true); - - if(command.is_access()) { - status_[0] = command.drive_head(); - } - } - - private: - void set(uint8_t flag, bool value, uint8_t &target) { - if(value) { - target |= flag; - } else { - target &= ~flag; - } - } - - uint8_t main_status_; - uint8_t status_[3]; + uint8_t main_status_; + uint8_t status_[3]; }; } diff --git a/Components/8272/i8272.cpp b/Components/8272/i8272.cpp index 6f7592b79..4bb484386 100644 --- a/Components/8272/i8272.cpp +++ b/Components/8272/i8272.cpp @@ -18,7 +18,7 @@ Log::Logger logger; using namespace Intel::i8272; -i8272::i8272(BusHandler &bus_handler, Cycles clock_rate) : +i8272::i8272(BusHandler &bus_handler, const Cycles clock_rate) : Storage::Disk::MFMController(clock_rate), bus_handler_(bus_handler) { posit_event(int(Event8272::CommandByte)); @@ -34,7 +34,7 @@ ClockingHint::Preference i8272::preferred_clocking() const { return is_sleeping_ ? ClockingHint::Preference::None : ClockingHint::Preference::JustInTime; } -void i8272::run_for(Cycles cycles) { +void i8272::run_for(const Cycles cycles) { Storage::Disk::MFMController::run_for(cycles); if(is_sleeping_) return; @@ -109,7 +109,7 @@ void i8272::run_for(Cycles cycles) { if(is_sleeping_) update_clocking_observer(); } -void i8272::write(int address, uint8_t value) { +void i8272::write(const int address, const uint8_t value) { // don't consider attempted sets to the status register if(!address) return; @@ -127,7 +127,7 @@ void i8272::write(int address, uint8_t value) { } } -uint8_t i8272::read(int address) { +uint8_t i8272::read(const int address) { if(address) { if(result_stack_.empty()) return 0xff; uint8_t result = result_stack_.back(); @@ -208,7 +208,7 @@ uint8_t i8272::read(int address) { drives_[active_drive_].head_unload_delay[active_head_] = MS_TO_CYCLES(head_unload_time_);\ } -void i8272::posit_event(int event_type) { +void i8272::posit_event(const int event_type) { if(event_type == int(Event::IndexHole)) index_hole_count_++; if(event_type == int(Event8272::NoLongerReady)) { status_.set(Status0::NotReady); diff --git a/Components/8272/i8272.hpp b/Components/8272/i8272.hpp index 6f4fd1d3a..dc1ac7dd3 100644 --- a/Components/8272/i8272.hpp +++ b/Components/8272/i8272.hpp @@ -20,116 +20,116 @@ namespace Intel::i8272 { class BusHandler { - public: - virtual ~BusHandler() = default; - virtual void set_dma_data_request([[maybe_unused]] bool drq) {} - virtual void set_interrupt([[maybe_unused]] bool irq) {} +public: + virtual ~BusHandler() = default; + virtual void set_dma_data_request([[maybe_unused]] bool drq) {} + virtual void set_interrupt([[maybe_unused]] bool irq) {} }; class i8272 : public Storage::Disk::MFMController { - public: - i8272(BusHandler &bus_handler, Cycles clock_rate); +public: + i8272(BusHandler &bus_handler, Cycles clock_rate); - void run_for(Cycles); + void run_for(Cycles); - void set_data_input(uint8_t value); - uint8_t get_data_output(); + void set_data_input(uint8_t value); + uint8_t get_data_output(); - void write(int address, uint8_t value); - uint8_t read(int address); + void write(int address, uint8_t value); + uint8_t read(int address); - void set_dma_acknowledge(bool dack); - void set_terminal_count(bool tc); + void set_dma_acknowledge(bool dack); + void set_terminal_count(bool tc); - ClockingHint::Preference preferred_clocking() const final; + ClockingHint::Preference preferred_clocking() const final; - protected: - virtual void select_drive(int number) = 0; +protected: + virtual void select_drive(int number) = 0; - private: - // The bus handler, for interrupt and DMA-driven usage. [TODO] - BusHandler &bus_handler_; - std::unique_ptr allocated_bus_handler_; +private: + // The bus handler, for interrupt and DMA-driven usage. [TODO] + BusHandler &bus_handler_; + std::unique_ptr allocated_bus_handler_; - // Status registers. - Status status_; + // Status registers. + Status status_; - // The incoming command. - CommandDecoder command_; + // The incoming command. + CommandDecoder command_; - // A buffer to accumulate the result. - std::vector result_stack_; - uint8_t input_ = 0; - bool has_input_ = false; - bool expects_input_ = false; + // A buffer to accumulate the result. + std::vector result_stack_; + uint8_t input_ = 0; + bool has_input_ = false; + bool expects_input_ = false; - // Event stream: the 8272-specific events, plus the current event state. - enum class Event8272: int { - CommandByte = (1 << 3), - Timer = (1 << 4), - ResultEmpty = (1 << 5), - NoLongerReady = (1 << 6) - }; - void posit_event(int type) final; - int interesting_event_mask_ = int(Event8272::CommandByte); - int resume_point_ = 0; - bool is_access_command_ = false; + // Event stream: the 8272-specific events, plus the current event state. + enum class Event8272: int { + CommandByte = (1 << 3), + Timer = (1 << 4), + ResultEmpty = (1 << 5), + NoLongerReady = (1 << 6) + }; + void posit_event(int type) final; + int interesting_event_mask_ = int(Event8272::CommandByte); + int resume_point_ = 0; + bool is_access_command_ = false; - // The counter used for ::Timer events. - Cycles::IntType delay_time_ = 0; + // The counter used for ::Timer events. + Cycles::IntType delay_time_ = 0; - // The connected drives. - struct Drive { - uint8_t head_position = 0; + // The connected drives. + struct Drive { + uint8_t head_position = 0; - // Seeking: persistent state. - enum Phase { - NotSeeking, - Seeking, - CompletedSeeking - } phase = NotSeeking; - bool did_seek = false; - bool seek_failed = false; + // Seeking: persistent state. + enum Phase { + NotSeeking, + Seeking, + CompletedSeeking + } phase = NotSeeking; + bool did_seek = false; + bool seek_failed = false; - // Seeking: transient state. - Cycles::IntType step_rate_counter = 0; - int steps_taken = 0; - int target_head_position = 0; // either an actual number, or -1 to indicate to step until track zero + // Seeking: transient state. + Cycles::IntType step_rate_counter = 0; + int steps_taken = 0; + int target_head_position = 0; // either an actual number, or -1 to indicate to step until track zero - // Head state. - Cycles::IntType head_unload_delay[2] = {0, 0}; - bool head_is_loaded[2] = {false, false}; + // Head state. + Cycles::IntType head_unload_delay[2] = {0, 0}; + bool head_is_loaded[2] = {false, false}; - } drives_[4]; - int drives_seeking_ = 0; + } drives_[4]; + int drives_seeking_ = 0; - /// @returns @c true if the selected drive, which is number @c drive, can stop seeking. - bool seek_is_satisfied(int drive); + /// @returns @c true if the selected drive, which is number @c drive, can stop seeking. + bool seek_is_satisfied(int drive); - // User-supplied parameters; as per the specify command. - int step_rate_time_ = 1; - int head_unload_time_ = 1; - int head_load_time_ = 1; - bool dma_mode_ = false; - bool is_executing_ = false; + // User-supplied parameters; as per the specify command. + int step_rate_time_ = 1; + int head_unload_time_ = 1; + int head_load_time_ = 1; + bool dma_mode_ = false; + bool is_executing_ = false; - // A count of head unload timers currently running. - int head_timers_running_ = 0; + // A count of head unload timers currently running. + int head_timers_running_ = 0; - // Transient storage and counters used while reading the disk. - uint8_t header_[6] = {0, 0, 0, 0, 0, 0}; - int distance_into_section_ = 0; - int index_hole_count_ = 0, index_hole_limit_ = 0; + // Transient storage and counters used while reading the disk. + uint8_t header_[6] = {0, 0, 0, 0, 0, 0}; + int distance_into_section_ = 0; + int index_hole_count_ = 0, index_hole_limit_ = 0; - // Keeps track of the drive and head in use during commands. - int active_drive_ = 0; - int active_head_ = 0; + // Keeps track of the drive and head in use during commands. + int active_drive_ = 0; + int active_head_ = 0; - // Internal registers. - uint8_t cylinder_ = 0, head_ = 0, sector_ = 0, size_ = 0; + // Internal registers. + uint8_t cylinder_ = 0, head_ = 0, sector_ = 0, size_ = 0; - // Master switch on not performing any work. - bool is_sleeping_ = false; + // Master switch on not performing any work. + bool is_sleeping_ = false; }; } diff --git a/Components/8530/z8530.cpp b/Components/8530/z8530.cpp index 8077f0df6..117e1999f 100644 --- a/Components/8530/z8530.cpp +++ b/Components/8530/z8530.cpp @@ -39,7 +39,7 @@ bool z8530::get_interrupt_line() const { A1 = C/D (i.e. control or data) */ -std::uint8_t z8530::read(int address) { +std::uint8_t z8530::read(const int address) { if(address & 2) { // Read data register for channel. return channels_[address & 1].read(true, pointer_); @@ -89,7 +89,7 @@ std::uint8_t z8530::read(int address) { return 0x00; } -void z8530::write(int address, std::uint8_t value) { +void z8530::write(const int address, const std::uint8_t value) { if(address & 2) { // Write data register for channel. This is completely independent // of whatever is going on over in the control realm. @@ -140,14 +140,14 @@ void z8530::write(int address, std::uint8_t value) { update_delegate(); } -void z8530::set_dcd(int port, bool level) { +void z8530::set_dcd(const int port, const bool level) { channels_[port].set_dcd(level); update_delegate(); } // MARK: - Channel implementations -uint8_t z8530::Channel::read(bool data, uint8_t pointer) { +uint8_t z8530::Channel::read(const bool data, const uint8_t pointer) { // If this is a data read, just return it. if(data) { return data_; @@ -232,7 +232,7 @@ uint8_t z8530::Channel::read(bool data, uint8_t pointer) { return 0x00; } -void z8530::Channel::write(bool data, uint8_t pointer, uint8_t value) { +void z8530::Channel::write(const bool data, const uint8_t pointer, const uint8_t value) { if(data) { data_ = value; return; @@ -400,7 +400,7 @@ void z8530::Channel::write(bool data, uint8_t pointer, uint8_t value) { } } -void z8530::Channel::set_dcd(bool level) { +void z8530::Channel::set_dcd(const bool level) { if(dcd_ == level) return; dcd_ = level; diff --git a/Components/8530/z8530.hpp b/Components/8530/z8530.hpp index 422074634..63e4bcf6d 100644 --- a/Components/8530/z8530.hpp +++ b/Components/8530/z8530.hpp @@ -16,95 +16,95 @@ namespace Zilog::SCC { Models the Zilog 8530 SCC, a serial adaptor. */ class z8530 { - public: - /* - **Interface for emulated machine.** +public: + /* + **Interface for emulated machine.** - Notes on addressing below: + Notes on addressing below: - There's no inherent ordering of the two 'address' lines, - A/B and C/D, but the methods below assume: + There's no inherent ordering of the two 'address' lines, + A/B and C/D, but the methods below assume: - A/B = A0 - C/D = A1 - */ + A/B = A0 + C/D = A1 + */ - /// Performs a read from the SCC; see above for conventions as to 'address'. - std::uint8_t read(int address); - /// Performs a write to the SCC; see above for conventions as to 'address'. - void write(int address, std::uint8_t value); - /// Resets the SCC. - void reset(); + /// Performs a read from the SCC; see above for conventions as to 'address'. + std::uint8_t read(int address); + /// Performs a write to the SCC; see above for conventions as to 'address'. + void write(int address, std::uint8_t value); + /// Resets the SCC. + void reset(); - /// @returns The current value of the status output: @c true for active; @c false for inactive. - bool get_interrupt_line() const; - - struct Delegate { - /*! - Communicates that @c scc now has the interrupt line status @c new_status. - */ - virtual void did_change_interrupt_status(z8530 *scc, bool new_status) = 0; - }; + /// @returns The current value of the status output: @c true for active; @c false for inactive. + bool get_interrupt_line() const; + struct Delegate { /*! - Sets the delegate for this SCC. If this is a new delegate it is sent - an immediate did_change_interrupt_status message, to get it - up to speed. + Communicates that @c scc now has the interrupt line status @c new_status. */ - void set_delegate(Delegate *delegate) { - if(delegate_ == delegate) return; - delegate_ = delegate; - delegate_->did_change_interrupt_status(this, get_interrupt_line()); - } + virtual void did_change_interrupt_status(z8530 *, bool new_status) = 0; + }; - /* - **Interface for serial port input.** - */ - void set_dcd(int port, bool level); + /*! + Sets the delegate for this SCC. If this is a new delegate it is sent + an immediate did_change_interrupt_status message, to get it + up to speed. + */ + void set_delegate(Delegate *const delegate) { + if(delegate_ == delegate) return; + delegate_ = delegate; + delegate_->did_change_interrupt_status(this, get_interrupt_line()); + } - private: - class Channel { - public: - uint8_t read(bool data, uint8_t pointer); - void write(bool data, uint8_t pointer, uint8_t value); - void set_dcd(bool level); - bool get_interrupt_line() const; + /* + **Interface for serial port input.** + */ + void set_dcd(int port, bool level); - private: - uint8_t data_ = 0xff; +private: + class Channel { + public: + uint8_t read(bool data, uint8_t pointer); + void write(bool data, uint8_t pointer, uint8_t value); + void set_dcd(bool level); + bool get_interrupt_line() const; - enum class Parity { - Even, Odd, Off - } parity_ = Parity::Off; + private: + uint8_t data_ = 0xff; - enum class StopBits { - Synchronous, OneBit, OneAndAHalfBits, TwoBits - } stop_bits_ = StopBits::Synchronous; + enum class Parity { + Even, Odd, Off + } parity_ = Parity::Off; - enum class Sync { - Monosync, Bisync, SDLC, External - } sync_mode_ = Sync::Monosync; + enum class StopBits { + Synchronous, OneBit, OneAndAHalfBits, TwoBits + } stop_bits_ = StopBits::Synchronous; - int clock_rate_multiplier_ = 1; + enum class Sync { + Monosync, Bisync, SDLC, External + } sync_mode_ = Sync::Monosync; - uint8_t interrupt_mask_ = 0; // i.e. Write Register 0x1. + int clock_rate_multiplier_ = 1; - uint8_t external_interrupt_mask_ = 0; // i.e. Write Register 0xf. - bool external_status_interrupt_ = false; - uint8_t external_interrupt_status_ = 0; + uint8_t interrupt_mask_ = 0; // i.e. Write Register 0x1. - bool dcd_ = false; - } channels_[2]; + uint8_t external_interrupt_mask_ = 0; // i.e. Write Register 0xf. + bool external_status_interrupt_ = false; + uint8_t external_interrupt_status_ = 0; - uint8_t pointer_ = 0; + bool dcd_ = false; + } channels_[2]; - uint8_t interrupt_vector_ = 0; + uint8_t pointer_ = 0; - uint8_t master_interrupt_control_ = 0; + uint8_t interrupt_vector_ = 0; - bool previous_interrupt_line_ = false; - void update_delegate(); - Delegate *delegate_ = nullptr; + uint8_t master_interrupt_control_ = 0; + + bool previous_interrupt_line_ = false; + void update_delegate(); + Delegate *delegate_ = nullptr; }; } diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 7cd0b9f30..84bbbdf6d 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -5455,7 +5455,7 @@ attributes = { BuildIndependentTargetsInParallel = YES; LastSwiftUpdateCheck = 0700; - LastUpgradeCheck = 1430; + LastUpgradeCheck = 1610; ORGANIZATIONNAME = "Thomas Harte"; TargetAttributes = { 4B055A691FAE763F0060FFFF = { @@ -6908,6 +6908,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_CXX_LANGUAGE_STANDARD = "c++17"; CLANG_ENABLE_MODULES = YES; @@ -6968,6 +6969,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_CXX_LANGUAGE_STANDARD = "c++17"; CLANG_ENABLE_MODULES = YES; diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal Kiosk.xcscheme b/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal Kiosk.xcscheme index 03d185594..9bce73717 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal Kiosk.xcscheme +++ b/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal Kiosk.xcscheme @@ -1,6 +1,6 @@ + LastUpgradeVersion = "1610" + version = "1.8"> diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock SignalTests.xcscheme b/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock SignalTests.xcscheme index 1e460a993..c5df9ece3 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock SignalTests.xcscheme +++ b/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock SignalTests.xcscheme @@ -1,6 +1,6 @@ - - - -