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; }; }