From 5545906063e46742c35079f010a6fd4e670e87a7 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 30 Nov 2024 15:53:58 -0500 Subject: [PATCH 1/2] Adopt new indentation, improve `const`ness. --- Components/68901/MFP68901.cpp | 22 +- Components/68901/MFP68901.hpp | 264 +++++----- Components/9918/9918.hpp | 102 ++-- Components/9918/Implementation/9918.cpp | 24 +- Components/9918/Implementation/9918Base.hpp | 25 +- .../9918/Implementation/AccessEnums.hpp | 8 +- .../9918/Implementation/ClockConverter.hpp | 118 ++--- Components/9918/Implementation/Draw.hpp | 22 +- Components/9918/Implementation/Fetch.hpp | 59 ++- .../9918/Implementation/PersonalityTraits.hpp | 10 +- Components/9918/Implementation/Storage.hpp | 485 +++++++++--------- .../9918/Implementation/YamahaCommands.hpp | 217 ++++---- Components/AY38910/AY38910.cpp | 37 +- Components/AY38910/AY38910.hpp | 186 +++---- Components/AppleClock/AppleClock.hpp | 449 ++++++++-------- Components/AudioToggle/AudioToggle.cpp | 4 +- Components/AudioToggle/AudioToggle.hpp | 38 +- Components/DiskII/DiskII.cpp | 10 +- Components/DiskII/DiskII.hpp | 144 +++--- Components/DiskII/DiskIIDrive.cpp | 6 +- Components/DiskII/DiskIIDrive.hpp | 16 +- Components/DiskII/IWM.cpp | 20 +- Components/DiskII/IWM.hpp | 102 ++-- .../DiskII/MacintoshDoubleDensityDrive.cpp | 10 +- .../DiskII/MacintoshDoubleDensityDrive.hpp | 50 +- 25 files changed, 1242 insertions(+), 1186 deletions(-) diff --git a/Components/68901/MFP68901.cpp b/Components/68901/MFP68901.cpp index 02ef79588..345890424 100644 --- a/Components/68901/MFP68901.cpp +++ b/Components/68901/MFP68901.cpp @@ -74,7 +74,7 @@ uint8_t MFP68901::read(int address) { return 0x00; } -void MFP68901::write(int address, uint8_t value) { +void MFP68901::write(int address, const uint8_t value) { address &= 0x1f; // Interrupt block: enabled and masked interrupts can be set; pending and in-service interrupts can be masked. @@ -181,7 +181,7 @@ void MFP68901::write(int address, uint8_t value) { } template -void MFP68901::run_timer_for(int cycles) { +void MFP68901::run_timer_for(const int cycles) { if(timers_[timer].mode >= TimerMode::Delay) { // This code applies the timer prescaling only. prescale_count is used to count // upwards rather than downwards for simplicity, but on the real hardware it's @@ -202,7 +202,7 @@ void MFP68901::run_timer_for(int cycles) { } } -void MFP68901::run_for(HalfCycles time) { +void MFP68901::run_for(const HalfCycles time) { cycles_left_ += time; const int cycles = int(cycles_left_.flush().as_integral()); @@ -220,7 +220,7 @@ HalfCycles MFP68901::next_sequence_point() { // MARK: - Timers -void MFP68901::set_timer_mode(int timer, TimerMode mode, int prescale, bool reset_timer) { +void MFP68901::set_timer_mode(const int timer, const TimerMode mode, const int prescale, const bool reset_timer) { logger.error().append("Timer %d mode set: %d; prescale: %d", timer, mode, prescale); timers_[timer].mode = mode; if(reset_timer) { @@ -236,19 +236,19 @@ void MFP68901::set_timer_mode(int timer, TimerMode mode, int prescale, bool rese timers_[timer].prescale = prescale; } -void MFP68901::set_timer_data(int timer, uint8_t value) { +void MFP68901::set_timer_data(const int timer, const uint8_t value) { if(timers_[timer].mode == TimerMode::Stopped) { timers_[timer].value = value; } timers_[timer].reload_value = value; } -uint8_t MFP68901::get_timer_data(int timer) { +uint8_t MFP68901::get_timer_data(const int timer) { return timers_[timer].value; } template -void MFP68901::set_timer_event_input(bool value) { +void MFP68901::set_timer_event_input(const bool value) { if(timers_[channel].event_input == value) return; timers_[channel].event_input = value; @@ -316,7 +316,7 @@ void MFP68901::decrement_timer(int amount) { } // MARK: - GPIP -void MFP68901::set_port_input(uint8_t input) { +void MFP68901::set_port_input(const uint8_t input) { gpip_input_ = input; reevaluate_gpip_interrupts(); } @@ -342,12 +342,12 @@ void MFP68901::reevaluate_gpip_interrupts() { // MARK: - Interrupts -void MFP68901::begin_interrupts(int interrupt) { +void MFP68901::begin_interrupts(const int interrupt) { interrupt_pending_ |= interrupt & interrupt_enable_; update_interrupts(); } -void MFP68901::end_interrupts(int interrupt) { +void MFP68901::end_interrupts(const int interrupt) { interrupt_pending_ &= ~interrupt; update_interrupts(); } @@ -403,6 +403,6 @@ int MFP68901::acknowledge_interrupt() { return (interrupt_vector_ & 0xf0) | uint8_t(selected); } -void MFP68901::set_interrupt_delegate(InterruptDelegate *delegate) { +void MFP68901::set_interrupt_delegate(InterruptDelegate *const delegate) { interrupt_delegate_ = delegate; } diff --git a/Components/68901/MFP68901.hpp b/Components/68901/MFP68901.hpp index 34f108d8c..635b3f806 100644 --- a/Components/68901/MFP68901.hpp +++ b/Components/68901/MFP68901.hpp @@ -24,162 +24,162 @@ class PortHandler { Models the Motorola 68901 Multi-Function Peripheral ('MFP'). */ class MFP68901: public ClockingHint::Source { - public: - /// @returns the result of a read from @c address. - uint8_t read(int address); +public: + /// @returns the result of a read from @c address. + uint8_t read(int address); - /// Performs a write of @c value to @c address. - void write(int address, uint8_t value); + /// Performs a write of @c value to @c address. + void write(int address, uint8_t value); - /// Advances the MFP by the supplied number of HalfCycles. - void run_for(HalfCycles); + /// Advances the MFP by the supplied number of HalfCycles. + void run_for(HalfCycles); - /// @returns the number of cycles until the next possible sequence point — the next time - /// at which the interrupt line _might_ change. This object conforms to ClockingHint::Source - /// so that mechanism can also be used to reduce the quantity of calls into this class. - /// - /// @discussion TODO, alas. - HalfCycles next_sequence_point(); + /// @returns the number of cycles until the next possible sequence point — the next time + /// at which the interrupt line _might_ change. This object conforms to ClockingHint::Source + /// so that mechanism can also be used to reduce the quantity of calls into this class. + /// + /// @discussion TODO, alas. + HalfCycles next_sequence_point(); - /// Sets the current level of either of the timer event inputs — TAI and TBI in datasheet terms. - template - void set_timer_event_input(bool value); + /// Sets the current level of either of the timer event inputs — TAI and TBI in datasheet terms. + template + void set_timer_event_input(bool value); - /// Sets a port handler, a receiver that will be notified upon any change in GPIP output. - /// - /// @discussion TODO. - void set_port_handler(PortHandler *); + /// Sets a port handler, a receiver that will be notified upon any change in GPIP output. + /// + /// @discussion TODO. + void set_port_handler(PortHandler *); - /// Sets the current input GPIP values. - void set_port_input(uint8_t); + /// Sets the current input GPIP values. + void set_port_input(uint8_t); - /// @returns the current GPIP output values. - /// - /// @discussion TODO. - uint8_t get_port_output(); + /// @returns the current GPIP output values. + /// + /// @discussion TODO. + uint8_t get_port_output(); - /// @returns @c true if the interrupt output is currently active; @c false otherwise.s - bool get_interrupt_line(); + /// @returns @c true if the interrupt output is currently active; @c false otherwise.s + bool get_interrupt_line(); - static constexpr int NoAcknowledgement = 0x100; + static constexpr int NoAcknowledgement = 0x100; - /// Communicates an interrupt acknowledge cycle. - /// - /// @returns the vector placed on the bus if any; @c NoAcknowledgement if nothing is loaded. - int acknowledge_interrupt(); + /// Communicates an interrupt acknowledge cycle. + /// + /// @returns the vector placed on the bus if any; @c NoAcknowledgement if nothing is loaded. + int acknowledge_interrupt(); - struct InterruptDelegate { - /// Informs the delegate of a change in the interrupt line of the nominated MFP. - virtual void mfp68901_did_change_interrupt_status(MFP68901 *) = 0; - }; - /// Sets a delegate that will be notified upon any change in the interrupt line. - void set_interrupt_delegate(InterruptDelegate *delegate); + struct InterruptDelegate { + /// Informs the delegate of a change in the interrupt line of the nominated MFP. + virtual void mfp68901_did_change_interrupt_status(MFP68901 *) = 0; + }; + /// Sets a delegate that will be notified upon any change in the interrupt line. + void set_interrupt_delegate(InterruptDelegate *); - // ClockingHint::Source. - ClockingHint::Preference preferred_clocking() const final; + // ClockingHint::Source. + ClockingHint::Preference preferred_clocking() const final; - private: - // MARK: - Timers - enum class TimerMode { - Stopped, EventCount, Delay, PulseWidth - }; - void set_timer_mode(int timer, TimerMode, int prescale, bool reset_timer); - void set_timer_data(int timer, uint8_t); - uint8_t get_timer_data(int timer); - template void decrement_timer(int amount); - template void run_timer_for(int cycles); +private: + // MARK: - Timers + enum class TimerMode { + Stopped, EventCount, Delay, PulseWidth + }; + void set_timer_mode(int timer, TimerMode, int prescale, bool reset_timer); + void set_timer_data(int timer, uint8_t); + uint8_t get_timer_data(int timer); + template void decrement_timer(int amount); + template void run_timer_for(int cycles); - struct Timer { - TimerMode mode = TimerMode::Stopped; - uint8_t value = 0; - uint8_t reload_value = 0; - int prescale = 1; - int prescale_count = 1; - bool event_input = false; - } timers_[4]; - uint8_t timer_ab_control_[2] = { 0, 0 }; - uint8_t timer_cd_control_ = 0; + struct Timer { + TimerMode mode = TimerMode::Stopped; + uint8_t value = 0; + uint8_t reload_value = 0; + int prescale = 1; + int prescale_count = 1; + bool event_input = false; + } timers_[4]; + uint8_t timer_ab_control_[2] = { 0, 0 }; + uint8_t timer_cd_control_ = 0; - HalfCycles cycles_left_; + HalfCycles cycles_left_; - // MARK: - GPIP - uint8_t gpip_input_ = 0; - uint8_t gpip_output_ = 0; - uint8_t gpip_active_edge_ = 0; - uint8_t gpip_direction_ = 0; - uint8_t gpip_interrupt_state_ = 0; + // MARK: - GPIP + uint8_t gpip_input_ = 0; + uint8_t gpip_output_ = 0; + uint8_t gpip_active_edge_ = 0; + uint8_t gpip_direction_ = 0; + uint8_t gpip_interrupt_state_ = 0; - void reevaluate_gpip_interrupts(); + void reevaluate_gpip_interrupts(); - // MARK: - Interrupts + // MARK: - Interrupts - InterruptDelegate *interrupt_delegate_ = nullptr; + InterruptDelegate *interrupt_delegate_ = nullptr; - // Ad hoc documentation: - // - // An interrupt becomes pending if it is enabled at the time it occurs. - // - // If a pending interrupt is enabled in the interrupt mask, a processor - // interrupt is generated. Otherwise no processor interrupt is generated. - // - // (Disabling a bit in the enabled mask also instantaneously clears anything - // in the pending mask.) - // - // The user can write to the pending interrupt register; a write - // masks whatever is there — so you can disable bits but you cannot set them. - // - // If the vector register's 'S' bit is set then software end-of-interrupt mode applies: - // Acknowledgement of an interrupt clears that interrupt's pending bit, but also sets - // its in-service bit. That bit will remain set until the user writes a zero to its position. - // If any bits are set in the in-service register, then they will prevent lower-priority - // interrupts from being signalled to the CPU. Further interrupts of the same or a higher - // priority may occur. - // - // If the vector register's 'S' bit is clear then automatic end-of-interrupt mode applies: - // Acknowledgement of an interrupt will automatically clear the corresponding - // pending bit. - // - int interrupt_enable_ = 0; - int interrupt_pending_ = 0; - int interrupt_mask_ = 0; - int interrupt_in_service_ = 0; - bool interrupt_line_ = false; - uint8_t interrupt_vector_ = 0; + // Ad hoc documentation: + // + // An interrupt becomes pending if it is enabled at the time it occurs. + // + // If a pending interrupt is enabled in the interrupt mask, a processor + // interrupt is generated. Otherwise no processor interrupt is generated. + // + // (Disabling a bit in the enabled mask also instantaneously clears anything + // in the pending mask.) + // + // The user can write to the pending interrupt register; a write + // masks whatever is there — so you can disable bits but you cannot set them. + // + // If the vector register's 'S' bit is set then software end-of-interrupt mode applies: + // Acknowledgement of an interrupt clears that interrupt's pending bit, but also sets + // its in-service bit. That bit will remain set until the user writes a zero to its position. + // If any bits are set in the in-service register, then they will prevent lower-priority + // interrupts from being signalled to the CPU. Further interrupts of the same or a higher + // priority may occur. + // + // If the vector register's 'S' bit is clear then automatic end-of-interrupt mode applies: + // Acknowledgement of an interrupt will automatically clear the corresponding + // pending bit. + // + int interrupt_enable_ = 0; + int interrupt_pending_ = 0; + int interrupt_mask_ = 0; + int interrupt_in_service_ = 0; + bool interrupt_line_ = false; + uint8_t interrupt_vector_ = 0; - enum Interrupt { - GPIP0 = (1 << 0), - GPIP1 = (1 << 1), - GPIP2 = (1 << 2), - GPIP3 = (1 << 3), - TimerD = (1 << 4), - TimerC = (1 << 5), - GPIP4 = (1 << 6), - GPIP5 = (1 << 7), + enum Interrupt { + GPIP0 = (1 << 0), + GPIP1 = (1 << 1), + GPIP2 = (1 << 2), + GPIP3 = (1 << 3), + TimerD = (1 << 4), + TimerC = (1 << 5), + GPIP4 = (1 << 6), + GPIP5 = (1 << 7), - TimerB = (1 << 8), - TransmitError = (1 << 9), - TransmitBufferEmpty = (1 << 10), - ReceiveError = (1 << 11), - ReceiveBufferFull = (1 << 12), - TimerA = (1 << 13), - GPIP6 = (1 << 14), - GPIP7 = (1 << 15), - }; - void begin_interrupts(int interrupt); - void end_interrupts(int interrupt); - void update_interrupts(); + TimerB = (1 << 8), + TransmitError = (1 << 9), + TransmitBufferEmpty = (1 << 10), + ReceiveError = (1 << 11), + ReceiveBufferFull = (1 << 12), + TimerA = (1 << 13), + GPIP6 = (1 << 14), + GPIP7 = (1 << 15), + }; + void begin_interrupts(int interrupt); + void end_interrupts(int interrupt); + void update_interrupts(); - /// @returns the most significant bit set in v, assuming it is one of the least significant 16. - inline static int msb16(int v) { - // Saturate all bits below the MSB. - v |= v >> 1; - v |= v >> 2; - v |= v >> 4; - v |= v >> 8; + /// @returns the most significant bit set in v, assuming it is one of the least significant 16. + static constexpr int msb16(int v) { + // Saturate all bits below the MSB. + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; - // Throw away lesser bits. - return (v+1) >> 1; - } + // Throw away lesser bits. + return (v+1) >> 1; + } }; } diff --git a/Components/9918/9918.hpp b/Components/9918/9918.hpp index a8ae753e7..e74e12d3d 100644 --- a/Components/9918/9918.hpp +++ b/Components/9918/9918.hpp @@ -54,75 +54,75 @@ namespace TI::TMS { See get_time_until_interrupt and get_interrupt_line for asynchronous operation options. */ template class TMS9918: private Base { - public: - /*! Constructs an instance of the VDP that behaves according to the templated personality. */ - TMS9918(); +public: + /*! Constructs an instance of the VDP that behaves according to the templated personality. */ + TMS9918(); - /*! Sets the TV standard for this TMS, if that is hard-coded in hardware. */ - void set_tv_standard(TVStandard standard); + /*! Sets the TV standard for this TMS, if that is hard-coded in hardware. */ + void set_tv_standard(TVStandard); - /*! Sets the scan target this TMS will post content to. */ - void set_scan_target(Outputs::Display::ScanTarget *); + /*! Sets the scan target this TMS will post content to. */ + void set_scan_target(Outputs::Display::ScanTarget *); - /*! Gets the current scan status. */ - Outputs::Display::ScanStatus get_scaled_scan_status() const; + /*! Gets the current scan status. */ + Outputs::Display::ScanStatus get_scaled_scan_status() const; - /*! Sets the type of CRT display. */ - void set_display_type(Outputs::Display::DisplayType); + /*! Sets the type of CRT display. */ + void set_display_type(Outputs::Display::DisplayType); - /*! Gets the type of CRT display. */ - Outputs::Display::DisplayType get_display_type() const; + /*! Gets the type of CRT display. */ + Outputs::Display::DisplayType get_display_type() const; - /*! - Runs the VDP for the number of cycles indicate; the input clock rate is implicitly assumed. + /*! + Runs the VDP for the number of cycles indicate; the input clock rate is implicitly assumed. - For everything except the Mega Drive VDP: - * the input clock rate should be 3579545 Hz, the NTSC colour clock rate. + For everything except the Mega Drive VDP: + * the input clock rate should be 3579545 Hz, the NTSC colour clock rate. - For the Mega Drive: - * the input clock rate should be around 7.6MHz; 15/7ths of the NTSC colour - clock rate for NTSC output and 12/7ths of the PAL colour clock rate for PAL output. - */ - void run_for(const HalfCycles cycles); + For the Mega Drive: + * the input clock rate should be around 7.6MHz; 15/7ths of the NTSC colour + clock rate for NTSC output and 12/7ths of the PAL colour clock rate for PAL output. + */ + void run_for(const HalfCycles); - /*! 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); - /*! Gets the current scan line; provided by the Sega VDPs only. */ - uint8_t get_current_line() const; + /*! Gets the current scan line; provided by the Sega VDPs only. */ + uint8_t get_current_line() const; - /*! Gets the current latched horizontal counter; provided by the Sega VDPs only. */ - uint8_t get_latched_horizontal_counter() const; + /*! Gets the current latched horizontal counter; provided by the Sega VDPs only. */ + uint8_t get_latched_horizontal_counter() const; - /*! Latches the current horizontal counter. */ - void latch_horizontal_counter(); + /*! Latches the current horizontal counter. */ + void latch_horizontal_counter(); - /*! - Returns the amount of time until @c get_interrupt_line would next change if - there are no interceding calls to @c write or to @c read. + /*! + Returns the amount of time until @c get_interrupt_line would next change if + there are no interceding calls to @c write or to @c read. - If get_interrupt_line is true now of if get_interrupt_line would - never return true, returns HalfCycles::max(). - */ - HalfCycles next_sequence_point() const; + If get_interrupt_line is true now of if get_interrupt_line would + never return true, returns HalfCycles::max(). + */ + HalfCycles next_sequence_point() const; - /*! - Returns the amount of time until the nominated line interrupt position is - reached on line @c line. If no line interrupt position is defined for - this VDP, returns the time until the 'beginning' of that line, whatever - that may mean. + /*! + Returns the amount of time until the nominated line interrupt position is + reached on line @c line. If no line interrupt position is defined for + this VDP, returns the time until the 'beginning' of that line, whatever + that may mean. - @line is relative to the first pixel line of the display and may be negative. - */ - HalfCycles get_time_until_line(int line); + @line is relative to the first pixel line of the display and may be negative. + */ + HalfCycles get_time_until_line(int line); - /*! - @returns @c true if the interrupt line is currently active; @c false otherwise. - */ - bool get_interrupt_line() const; + /*! + @returns @c true if the interrupt line is currently active; @c false otherwise. + */ + bool get_interrupt_line() const; }; } diff --git a/Components/9918/Implementation/9918.cpp b/Components/9918/Implementation/9918.cpp index 34de778a1..f8b36eaf7 100644 --- a/Components/9918/Implementation/9918.cpp +++ b/Components/9918/Implementation/9918.cpp @@ -86,7 +86,7 @@ TMS9918::TMS9918() { } template -void TMS9918::set_tv_standard(TVStandard standard) { +void TMS9918::set_tv_standard(const TVStandard standard) { // TODO: the Yamaha is programmable on this at runtime. this->tv_standard_ = standard; switch(standard) { @@ -104,7 +104,7 @@ void TMS9918::set_tv_standard(TVStandard standard) { } template -void TMS9918::set_scan_target(Outputs::Display::ScanTarget *scan_target) { +void TMS9918::set_scan_target(Outputs::Display::ScanTarget *const scan_target) { this->crt_.set_scan_target(scan_target); } @@ -118,7 +118,7 @@ Outputs::Display::ScanStatus TMS9918::get_scaled_scan_status() cons } template -void TMS9918::set_display_type(Outputs::Display::DisplayType display_type) { +void TMS9918::set_display_type(const Outputs::Display::DisplayType display_type) { this->crt_.set_display_type(display_type); } @@ -137,7 +137,7 @@ void SpriteBuffer::reset_sprite_collection() { } template -void Base::posit_sprite(int sprite_number, int sprite_position, uint8_t screen_row) { +void Base::posit_sprite(const int sprite_number, const int sprite_position, const uint8_t screen_row) { // Evaluation of visibility of sprite 0 is always the first step in // populating a sprite buffer; so use it to uncork a new one. if(!sprite_number) { @@ -661,7 +661,7 @@ void TMS9918::run_for(const HalfCycles cycles) { } template -void Base::output_border(int cycles, [[maybe_unused]] uint32_t cram_dot) { +void Base::output_border(int cycles, [[maybe_unused]] const uint32_t cram_dot) { cycles = from_internal(cycles); uint32_t border_colour; @@ -694,7 +694,7 @@ void Base::output_border(int cycles, [[maybe_unused]] uint32_t cram // MARK: - External interface. template -int Base::masked_address(int address) const { +int Base::masked_address(const int address) const { if constexpr (is_yamaha_vdp(personality)) { return address & 3; } else { @@ -703,7 +703,7 @@ int Base::masked_address(int address) const { } template -void Base::write_vram(uint8_t value) { +void Base::write_vram(const uint8_t value) { write_phase_ = false; // Enqueue the write to occur at the next available slot. @@ -713,7 +713,7 @@ void Base::write_vram(uint8_t value) { } template -void Base::commit_register(int reg, uint8_t value) { +void Base::commit_register(int reg, const uint8_t value) { if constexpr (is_yamaha_vdp(personality)) { reg &= 0x3f; } else if constexpr (is_sega_vdp(personality)) { @@ -1017,7 +1017,7 @@ void Base::commit_register(int reg, uint8_t value) { } template -void Base::write_register(uint8_t value) { +void Base::write_register(const uint8_t value) { // Writes to address 1 are performed in pairs; if this is the // low byte of a value, store it and wait for the high byte. if(!write_phase_) { @@ -1068,7 +1068,7 @@ void Base::write_register(uint8_t value) { } template -void Base::write_palette(uint8_t value) { +void Base::write_palette(const uint8_t value) { if constexpr (is_yamaha_vdp(personality)) { if(!Storage::palette_write_phase_) { Storage::new_colour_ = value; @@ -1091,7 +1091,7 @@ void Base::write_palette(uint8_t value) { } template -void Base::write_register_indirect([[maybe_unused]] uint8_t value) { +void Base::write_register_indirect([[maybe_unused]] const uint8_t value) { if constexpr (is_yamaha_vdp(personality)) { // Register 17 cannot be written to indirectly. if(Storage::indirect_register_ != 17) { @@ -1188,7 +1188,7 @@ uint8_t Base::read_register() { } template -uint8_t TMS9918::read(int address) { +uint8_t TMS9918::read(const int address) { const int target = this->masked_address(address); if(target < 2) { diff --git a/Components/9918/Implementation/9918Base.hpp b/Components/9918/Implementation/9918Base.hpp index ec63e267f..24fa31e11 100644 --- a/Components/9918/Implementation/9918Base.hpp +++ b/Components/9918/Implementation/9918Base.hpp @@ -41,7 +41,7 @@ template struct Base: public Storage { static constexpr int output_lag = 11; // i.e. pixel output will occur 11 cycles // after corresponding data read. - static constexpr uint32_t palette_pack(uint8_t r, uint8_t g, uint8_t b) { + static constexpr uint32_t palette_pack(const uint8_t r, const uint8_t g, const uint8_t b) { #if TARGET_RT_BIG_ENDIAN return uint32_t((r << 24) | (g << 16) | (b << 8)); #else @@ -85,7 +85,7 @@ template struct Base: public Storage { /// Mutates @c target such that @c source replaces the @c length bits that currently start /// at bit @c shift . Subsequently ensures @c target is constrained by the /// applicable @c memory_mask. - template void install_field(AddressT &target, uint8_t source) { + template void install_field(AddressT &target, const uint8_t source) { static_assert(length > 0 && length <= 8); constexpr auto source_mask = (1 << length) - 1; constexpr auto mask = AddressT(~(source_mask << shift)); @@ -302,11 +302,11 @@ template struct Base: public Storage { return ScreenMode::Blank; } - static AddressT rotate(AddressT address) { + static AddressT rotate(const AddressT address) { return AddressT((address >> 1) | (address << 16)) & memory_mask(personality); } - AddressT command_address(Vector location, bool expansion) const { + AddressT command_address(const Vector location, const bool expansion) const { if constexpr (is_yamaha_vdp(personality)) { switch(this->underlying_mode_) { default: @@ -345,7 +345,7 @@ template struct Base: public Storage { } } - uint8_t extract_colour(uint8_t byte, Vector location) const { + uint8_t extract_colour(const uint8_t byte, const Vector location) const { switch(this->screen_mode_) { default: case ScreenMode::YamahaGraphics4: // 256 pixels @ 4bpp @@ -360,7 +360,7 @@ template struct Base: public Storage { } } - std::pair command_colour_mask(Vector location) const { + std::pair command_colour_mask(const Vector location) const { if constexpr (is_yamaha_vdp(personality)) { auto &context = Storage::command_context_; auto colour = context.latched_colour.has_value() ? context.latched_colour : context.colour; @@ -394,7 +394,7 @@ template struct Base: public Storage { } } - void do_external_slot(int access_column) { + void do_external_slot(const int access_column) { // Don't do anything if the required time for the access to become executable // has yet to pass. if(queued_access_ == MemoryAccess::None || access_column < minimum_access_column_) { @@ -588,7 +588,7 @@ template struct Base: public Storage { /// /// i.e. it provides standard glue to enter a fetch sequence at any point, while the fetches themselves are templated on the cycle /// at which they appear for neater expression. - template void dispatch(Fetcher &fetcher, int start, int end); + template void dispatch(Fetcher &, int start, int end); // Various fetchers. template void fetch_tms_refresh(uint8_t y, int start, int end); @@ -616,7 +616,14 @@ template struct Base: public Storage { template void draw_yamaha(uint8_t y, int start, int end); void draw_yamaha(uint8_t y, int start, int end); - template void draw_sprites(uint8_t y, int start, int end, const std::array &palette, int *colour_buffer = nullptr); + template + void draw_sprites( + uint8_t y, + int start, + int end, + const std::array &palette, + int *colour_buffer = nullptr + ); }; } diff --git a/Components/9918/Implementation/AccessEnums.hpp b/Components/9918/Implementation/AccessEnums.hpp index b6c104dc7..f53b14b51 100644 --- a/Components/9918/Implementation/AccessEnums.hpp +++ b/Components/9918/Implementation/AccessEnums.hpp @@ -36,7 +36,7 @@ enum class ScreenMode { YamahaGraphics2 = Graphics, }; -constexpr int pixels_per_byte(ScreenMode mode) { +constexpr int pixels_per_byte(const ScreenMode mode) { switch(mode) { default: case ScreenMode::Blank: return 1; @@ -54,7 +54,7 @@ constexpr int pixels_per_byte(ScreenMode mode) { } } -constexpr int width(ScreenMode mode) { +constexpr int width(const ScreenMode mode) { switch(mode) { default: case ScreenMode::Blank: return 0; @@ -72,11 +72,11 @@ constexpr int width(ScreenMode mode) { } } -constexpr bool interleaves_banks(ScreenMode mode) { +constexpr bool interleaves_banks(const ScreenMode mode) { return mode == ScreenMode::YamahaGraphics6 || mode == ScreenMode::YamahaGraphics7; } -constexpr bool is_text(ScreenMode mode) { +constexpr bool is_text(const ScreenMode mode) { return mode == ScreenMode::Text || mode == ScreenMode::YamahaText80; } diff --git a/Components/9918/Implementation/ClockConverter.hpp b/Components/9918/Implementation/ClockConverter.hpp index 9d7699749..3b4253583 100644 --- a/Components/9918/Implementation/ClockConverter.hpp +++ b/Components/9918/Implementation/ClockConverter.hpp @@ -89,77 +89,77 @@ template constexpr int from an error term be tracked. */ template class ClockConverter { - public: - /*! - Given that another @c source external **half-cycles** has occurred, - indicates how many complete internal **cycles** have additionally elapsed - since the last call to @c to_internal. +public: + /*! + Given that another @c source external **half-cycles** has occurred, + indicates how many complete internal **cycles** have additionally elapsed + since the last call to @c to_internal. - E.g. for the TMS, @c source will count 456 ticks per line, and the internal clock - runs at 342 ticks per line, so the proper conversion is to multiply by 3/4. - */ - int to_internal(int source) { - switch(personality) { - // Default behaviour is to apply a multiplication by 3/4; - // this is correct for the TMS and Sega VDPs other than the Mega Drive. - default: { - const int result = source * 3 + cycles_error_; - cycles_error_ = result & 3; - return result >> 2; - } + E.g. for the TMS, @c source will count 456 ticks per line, and the internal clock + runs at 342 ticks per line, so the proper conversion is to multiply by 3/4. + */ + int to_internal(const int source) { + switch(personality) { + // Default behaviour is to apply a multiplication by 3/4; + // this is correct for the TMS and Sega VDPs other than the Mega Drive. + default: { + const int result = source * 3 + cycles_error_; + cycles_error_ = result & 3; + return result >> 2; + } - // The two Yamaha chips have an internal clock that is four times - // as fast as the TMS, therefore a stateless translation is possible. - case Personality::V9938: - case Personality::V9958: - return source * 3; + // The two Yamaha chips have an internal clock that is four times + // as fast as the TMS, therefore a stateless translation is possible. + case Personality::V9938: + case Personality::V9958: + return source * 3; - // The Mega Drive runs at 3420 master clocks per line, which is then - // divided by 4 or 5 depending on other state. That's 7 times the - // rate provided to the CPU; given that the input is in half-cycles - // the proper multiplier is therefore 3.5. - case Personality::MDVDP: { - const int result = source * 7 + cycles_error_; - cycles_error_ = result & 1; - return result >> 1; - } + // The Mega Drive runs at 3420 master clocks per line, which is then + // divided by 4 or 5 depending on other state. That's 7 times the + // rate provided to the CPU; given that the input is in half-cycles + // the proper multiplier is therefore 3.5. + case Personality::MDVDP: { + const int result = source * 7 + cycles_error_; + cycles_error_ = result & 1; + return result >> 1; } } + } - /*! - Provides the number of external cycles that need to begin from now in order to - get at least @c internal_cycles into the future. - */ - HalfCycles half_cycles_before_internal_cycles(int internal_cycles) const { - // Logic here correlates with multipliers as per @c to_internal. - switch(personality) { - default: - // Relative to the external clock multiplied by 3, it will definitely take this - // many cycles to complete a further (internal_cycles - 1) after the current one. - internal_cycles = (internal_cycles - 1) << 2; + /*! + Provides the number of external cycles that need to begin from now in order to + get at least @c internal_cycles into the future. + */ + HalfCycles half_cycles_before_internal_cycles(int internal_cycles) const { + // Logic here correlates with multipliers as per @c to_internal. + switch(personality) { + default: + // Relative to the external clock multiplied by 3, it will definitely take this + // many cycles to complete a further (internal_cycles - 1) after the current one. + internal_cycles = (internal_cycles - 1) << 2; - // It will also be necessary to complete the current one. - internal_cycles += 4 - cycles_error_; + // It will also be necessary to complete the current one. + internal_cycles += 4 - cycles_error_; - // Round up to get the first external cycle after - // the number of internal_cycles has elapsed. - return HalfCycles((internal_cycles + 2) / 3); + // Round up to get the first external cycle after + // the number of internal_cycles has elapsed. + return HalfCycles((internal_cycles + 2) / 3); - case Personality::V9938: - case Personality::V9958: - return HalfCycles((internal_cycles + 2) / 3); + case Personality::V9938: + case Personality::V9958: + return HalfCycles((internal_cycles + 2) / 3); - case Personality::MDVDP: - internal_cycles = (internal_cycles - 1) << 1; - internal_cycles += 2 - cycles_error_; - return HalfCycles((internal_cycles + 6) / 7); - } + case Personality::MDVDP: + internal_cycles = (internal_cycles - 1) << 1; + internal_cycles += 2 - cycles_error_; + return HalfCycles((internal_cycles + 6) / 7); } + } - private: - // Holds current residue in conversion from the external to - // internal clock. - int cycles_error_ = 0; +private: + // Holds current residue in conversion from the external to + // internal clock. + int cycles_error_ = 0; }; } diff --git a/Components/9918/Implementation/Draw.hpp b/Components/9918/Implementation/Draw.hpp index 7204e8b28..7bf93a85a 100644 --- a/Components/9918/Implementation/Draw.hpp +++ b/Components/9918/Implementation/Draw.hpp @@ -14,7 +14,13 @@ namespace TI::TMS { template template -void Base::draw_sprites([[maybe_unused]] uint8_t y, int start, int end, const std::array &palette, int *colour_buffer) { +void Base::draw_sprites( + [[maybe_unused]] const uint8_t y, + const int start, + const int end, + const std::array &palette, + int *const colour_buffer +) { if(!draw_line_buffer_->sprites) { return; } @@ -255,7 +261,7 @@ void Base::draw_sprites([[maybe_unused]] uint8_t y, int start, int template template -void Base::draw_tms_character(int start, int end) { +void Base::draw_tms_character(const int start, const int end) { auto &line_buffer = *draw_line_buffer_; // Paint the background tiles. @@ -304,7 +310,7 @@ void Base::draw_tms_character(int start, int end) { template template -void Base::draw_tms_text(int start, int end) { +void Base::draw_tms_text(const int start, const int end) { auto &line_buffer = *draw_line_buffer_; uint32_t colours[2][2] = { {palette()[background_colour_], palette()[text_colour_]}, @@ -345,7 +351,11 @@ void Base::draw_tms_text(int start, int end) { // MARK: - Master System template -void Base::draw_sms([[maybe_unused]] int start, [[maybe_unused]] int end, [[maybe_unused]] uint32_t cram_dot) { +void Base::draw_sms( + [[maybe_unused]] const int start, + [[maybe_unused]] const int end, + [[maybe_unused]] const uint32_t cram_dot +) { if constexpr (is_sega_vdp(personality)) { int colour_buffer[256]; auto &line_buffer = *draw_line_buffer_; @@ -449,7 +459,7 @@ void Base::draw_sms([[maybe_unused]] int start, [[maybe_unused]] in template template -void Base::draw_yamaha(uint8_t y, int start, int end) { +void Base::draw_yamaha(const uint8_t y, int start, int end) { [[maybe_unused]] const auto active_palette = palette(); const int sprite_start = start >> 2; const int sprite_end = end >> 2; @@ -536,7 +546,7 @@ void Base::draw_yamaha(uint8_t y, int start, int end) { } template -void Base::draw_yamaha(uint8_t y, int start, int end) { +void Base::draw_yamaha(const uint8_t y, const int start, const int end) { if constexpr (is_yamaha_vdp(personality)) { switch(draw_line_buffer_->screen_mode) { // Modes that are the same (or close enough) to those on the TMS. diff --git a/Components/9918/Implementation/Fetch.hpp b/Components/9918/Implementation/Fetch.hpp index 5d226bd1b..38bf0720d 100644 --- a/Components/9918/Implementation/Fetch.hpp +++ b/Components/9918/Implementation/Fetch.hpp @@ -57,23 +57,40 @@ template void Base::dispatch(Seq switch(start) { default: assert(false); - index(0); index(1); index(2); index(3); index(4); index(5); index(6); index(7); index(8); index(9); - index(10); index(11); index(12); index(13); index(14); index(15); index(16); index(17); index(18); index(19); - index(20); index(21); index(22); index(23); index(24); index(25); index(26); index(27); index(28); index(29); - index(30); index(31); index(32); index(33); index(34); index(35); index(36); index(37); index(38); index(39); - index(40); index(41); index(42); index(43); index(44); index(45); index(46); index(47); index(48); index(49); - index(50); index(51); index(52); index(53); index(54); index(55); index(56); index(57); index(58); index(59); - index(60); index(61); index(62); index(63); index(64); index(65); index(66); index(67); index(68); index(69); - index(70); index(71); index(72); index(73); index(74); index(75); index(76); index(77); index(78); index(79); - index(80); index(81); index(82); index(83); index(84); index(85); index(86); index(87); index(88); index(89); - index(90); index(91); index(92); index(93); index(94); index(95); index(96); index(97); index(98); index(99); - index(100); index(101); index(102); index(103); index(104); index(105); index(106); index(107); index(108); index(109); - index(110); index(111); index(112); index(113); index(114); index(115); index(116); index(117); index(118); index(119); - index(120); index(121); index(122); index(123); index(124); index(125); index(126); index(127); index(128); index(129); - index(130); index(131); index(132); index(133); index(134); index(135); index(136); index(137); index(138); index(139); - index(140); index(141); index(142); index(143); index(144); index(145); index(146); index(147); index(148); index(149); - index(150); index(151); index(152); index(153); index(154); index(155); index(156); index(157); index(158); index(159); - index(160); index(161); index(162); index(163); index(164); index(165); index(166); index(167); index(168); index(169); + index(0); index(1); index(2); index(3); index(4); + index(5); index(6); index(7); index(8); index(9); + index(10); index(11); index(12); index(13); index(14); + index(15); index(16); index(17); index(18); index(19); + index(20); index(21); index(22); index(23); index(24); + index(25); index(26); index(27); index(28); index(29); + index(30); index(31); index(32); index(33); index(34); + index(35); index(36); index(37); index(38); index(39); + index(40); index(41); index(42); index(43); index(44); + index(45); index(46); index(47); index(48); index(49); + index(50); index(51); index(52); index(53); index(54); + index(55); index(56); index(57); index(58); index(59); + index(60); index(61); index(62); index(63); index(64); + index(65); index(66); index(67); index(68); index(69); + index(70); index(71); index(72); index(73); index(74); + index(75); index(76); index(77); index(78); index(79); + index(80); index(81); index(82); index(83); index(84); + index(85); index(86); index(87); index(88); index(89); + index(90); index(91); index(92); index(93); index(94); + index(95); index(96); index(97); index(98); index(99); + index(100); index(101); index(102); index(103); index(104); + index(105); index(106); index(107); index(108); index(109); + index(110); index(111); index(112); index(113); index(114); + index(115); index(116); index(117); index(118); index(119); + index(120); index(121); index(122); index(123); index(124); + index(125); index(126); index(127); index(128); index(129); + index(130); index(131); index(132); index(133); index(134); + index(135); index(136); index(137); index(138); index(139); + index(140); index(141); index(142); index(143); index(144); + index(145); index(146); index(147); index(148); index(149); + index(150); index(151); index(152); index(153); index(154); + index(155); index(156); index(157); index(158); index(159); + index(160); index(161); index(162); index(163); index(164); + index(165); index(166); index(167); index(168); index(169); index(170); } @@ -108,7 +125,7 @@ template struct CharacterFetcher { using AddressT = typename Base::AddressT; - CharacterFetcher(Base *base, uint8_t y) : + CharacterFetcher(Base *const base, const uint8_t y) : base(base), y(y), row_base(base->pattern_name_address_ & bits<10>(AddressT((y << 2)&~31))) @@ -137,15 +154,15 @@ struct CharacterFetcher { } } - void fetch_name(int column) { + void fetch_name(const int column) { base->tile_offset_ = base->ram_[row_base + AddressT(column)]; } - void fetch_pattern(int column) { + void fetch_pattern(const int column) { base->fetch_line_buffer_->tiles.patterns[column][0] = base->ram_[pattern_base + AddressT(base->tile_offset_ << 3)]; } - void fetch_colour(int column) { + void fetch_colour(const int column) { base->fetch_line_buffer_->tiles.patterns[column][1] = base->ram_[colour_base + AddressT((base->tile_offset_ << 3) >> colour_name_shift)]; } diff --git a/Components/9918/Implementation/PersonalityTraits.hpp b/Components/9918/Implementation/PersonalityTraits.hpp index 6d035f7a4..64fe3d4b2 100644 --- a/Components/9918/Implementation/PersonalityTraits.hpp +++ b/Components/9918/Implementation/PersonalityTraits.hpp @@ -11,16 +11,16 @@ namespace TI::TMS { // Genus determinants for the various personalityes. -constexpr bool is_sega_vdp(Personality p) { +constexpr bool is_sega_vdp(const Personality p) { return p >= Personality::SMSVDP; } -constexpr bool is_yamaha_vdp(Personality p) { +constexpr bool is_yamaha_vdp(const Personality p) { return p == Personality::V9938 || p == Personality::V9958; } // i.e. one with the original internal timings. -constexpr bool is_classic_vdp(Personality p) { +constexpr bool is_classic_vdp(const Personality p) { return p == Personality::TMS9918A || p == Personality::SMSVDP || @@ -28,7 +28,7 @@ constexpr bool is_classic_vdp(Personality p) { p == Personality::GGVDP; } -constexpr size_t memory_size(Personality p) { +constexpr size_t memory_size(const Personality p) { switch(p) { case TI::TMS::TMS9918A: case TI::TMS::SMSVDP: @@ -41,7 +41,7 @@ constexpr size_t memory_size(Personality p) { return 0; } -constexpr size_t memory_mask(Personality p) { +constexpr size_t memory_mask(const Personality p) { return memory_size(p) - 1; } diff --git a/Components/9918/Implementation/Storage.hpp b/Components/9918/Implementation/Storage.hpp index c1260eb99..186e597bb 100644 --- a/Components/9918/Implementation/Storage.hpp +++ b/Components/9918/Implementation/Storage.hpp @@ -28,269 +28,269 @@ template <> struct Storage { }; struct YamahaFetcher { - public: - /// Describes an _observable_ memory access event. i.e. anything that it is safe - /// (and convenient) to treat as atomic in between external slots. - struct Event { - /// Offset of the _beginning_ of the event. Not completely arbitrarily: this is when - /// external data must be ready by in order to take part in those slots. - uint16_t offset = 1368; - enum class Type: uint8_t { - /// A slot for reading or writing data on behalf of the CPU or the command engine. - External, +public: + /// Describes an _observable_ memory access event. i.e. anything that it is safe + /// (and convenient) to treat as atomic in between external slots. + struct Event { + /// Offset of the _beginning_ of the event. Not completely arbitrarily: this is when + /// external data must be ready by in order to take part in those slots. + uint16_t offset = 1368; + enum class Type: uint8_t { + /// A slot for reading or writing data on behalf of the CPU or the command engine. + External, - // - // Sprites. - // - SpriteY, - SpriteLocation, - SpritePattern, + // + // Sprites. + // + SpriteY, + SpriteLocation, + SpritePattern, - // - // Backgrounds. - // - Name, - Colour, - Pattern, - } type = Type::External; - uint8_t id = 0; + // + // Backgrounds. + // + Name, + Colour, + Pattern, + } type = Type::External; + uint8_t id = 0; - constexpr Event(Type type, uint8_t id = 0) noexcept : - type(type), - id(id) {} + constexpr Event(Type type, uint8_t id = 0) noexcept : + type(type), + id(id) {} - constexpr Event() noexcept = default; - }; + constexpr Event() noexcept = default; + }; - // State that tracks fetching position within a line. - const Event *next_event_ = nullptr; + // State that tracks fetching position within a line. + const Event *next_event_ = nullptr; - // Sprite collection state. - bool sprites_enabled_ = true; + // Sprite collection state. + bool sprites_enabled_ = true; - protected: - /// @return 1 + the number of times within a line that @c GeneratorT produces an event. - template static constexpr size_t events_size() { - size_t size = 0; - for(int c = 0; c < 1368; c++) { - const auto event_type = GeneratorT::event(c); - size += event_type.has_value(); - } - return size + 1; +protected: + /// @return 1 + the number of times within a line that @c GeneratorT produces an event. + template static constexpr size_t events_size() { + size_t size = 0; + for(int c = 0; c < 1368; c++) { + const auto event_type = GeneratorT::event(c); + size += event_type.has_value(); } + return size + 1; + } - /// @return An array of all events generated by @c GeneratorT in line order. - template ()> - static constexpr std::array events() { - std::array result{}; - size_t index = 0; - for(int c = 0; c < 1368; c++) { - // Specific personality doesn't matter here; both Yamahas use the same internal timing. - const auto event = GeneratorT::event(from_internal(c)); - if(!event) { - continue; - } - result[index] = *event; - result[index].offset = uint16_t(c); - ++index; + /// @return An array of all events generated by @c GeneratorT in line order. + template ()> + static constexpr std::array events() { + std::array result{}; + size_t index = 0; + for(int c = 0; c < 1368; c++) { + // Specific personality doesn't matter here; both Yamahas use the same internal timing. + const auto event = GeneratorT::event(from_internal(c)); + if(!event) { + continue; } - result[index] = Event(); - return result; + result[index] = *event; + result[index].offset = uint16_t(c); + ++index; } + result[index] = Event(); + return result; + } - struct StandardGenerators { - static constexpr std::optional external_every_eight(int index) { - if(index & 7) return std::nullopt; + struct StandardGenerators { + static constexpr std::optional external_every_eight(int index) { + if(index & 7) return std::nullopt; + return Event::Type::External; + } + }; + + struct RefreshGenerator { + static constexpr std::optional event(const int grauw_index) { + // From 0 to 126: CPU/CMD slots at every cycle divisible by 8. + if(grauw_index < 126) { + return StandardGenerators::external_every_eight(grauw_index - 0); + } + + // From 164 to 1234: eight-cycle windows, the first 15 of each 16 being + // CPU/CMD and the final being refresh. + if(grauw_index >= 164 && grauw_index < 1234) { + const int offset = grauw_index - 164; + if(offset & 7) return std::nullopt; + if(((offset >> 3) & 15) == 15) return std::nullopt; return Event::Type::External; } - }; - struct RefreshGenerator { - static constexpr std::optional event(int grauw_index) { - // From 0 to 126: CPU/CMD slots at every cycle divisible by 8. - if(grauw_index < 126) { - return StandardGenerators::external_every_eight(grauw_index - 0); - } - - // From 164 to 1234: eight-cycle windows, the first 15 of each 16 being - // CPU/CMD and the final being refresh. - if(grauw_index >= 164 && grauw_index < 1234) { - const int offset = grauw_index - 164; - if(offset & 7) return std::nullopt; - if(((offset >> 3) & 15) == 15) return std::nullopt; - return Event::Type::External; - } - - // From 1268 to 1330: CPU/CMD slots at every cycle divisible by 8. - if(grauw_index >= 1268 && grauw_index < 1330) { - return StandardGenerators::external_every_eight(grauw_index - 1268); - } - - // A CPU/CMD at 1334. - if(grauw_index == 1334) { - return Event::Type::External; - } - - // From 1344 to 1366: CPU/CMD slots every cycle divisible by 8. - if(grauw_index >= 1344 && grauw_index < 1366) { - return StandardGenerators::external_every_eight(grauw_index - 1344); - } - - // Otherwise: nothing. - return std::nullopt; + // From 1268 to 1330: CPU/CMD slots at every cycle divisible by 8. + if(grauw_index >= 1268 && grauw_index < 1330) { + return StandardGenerators::external_every_eight(grauw_index - 1268); } - }; - template struct BitmapGenerator { - static constexpr std::optional event(int grauw_index) { - if(!include_sprites) { - // Various standard zones of one-every-eight external slots. - if(grauw_index < 124) { - return StandardGenerators::external_every_eight(grauw_index + 2); - } - if(grauw_index > 1266) { - return StandardGenerators::external_every_eight(grauw_index - 1266); - } - } else { - // This records collection points for all data for selected sprites. - // There's only four of them (each site covering two sprites), - // so it's clearer just to be explicit. - // - // There's also a corresponding number of extra external slots to spell out. - switch(grauw_index) { - default: break; - case 1238: return Event(Event::Type::SpriteLocation, 0); - case 1302: return Event(Event::Type::SpriteLocation, 2); - case 2: return Event(Event::Type::SpriteLocation, 4); - case 66: return Event(Event::Type::SpriteLocation, 6); - case 1270: return Event(Event::Type::SpritePattern, 0); - case 1338: return Event(Event::Type::SpritePattern, 2); - case 34: return Event(Event::Type::SpritePattern, 4); - case 98: return Event(Event::Type::SpritePattern, 6); - case 1264: case 1330: case 28: case 92: - return Event::Type::External; - } - } - - if(grauw_index >= 162 && grauw_index < 176) { - return StandardGenerators::external_every_eight(grauw_index - 162); - } - - // Everywhere else the pattern is: - // - // external or sprite y, external, data block - // - // Subject to caveats: - // - // 1) the first data block is just a dummy fetch with no side effects, - // so this emulator declines to record it; and - // 2) every fourth block, the second external is actually a refresh. - // - if(grauw_index >= 182 && grauw_index < 1238) { - const int offset = grauw_index - 182; - const int block = offset / 32; - const int sub_block = offset & 31; - - switch(sub_block) { - default: return std::nullopt; - case 0: - if(include_sprites) { - // Don't include the sprite post-amble (i.e. a spurious read with no side effects). - if(block < 32) { - return Event(Event::Type::SpriteY, uint8_t(block)); - } - } else { - return Event::Type::External; - } - case 6: - if((block & 3) != 3) { - return Event::Type::External; - } - break; - case 12: - if(block) { - return Event(Event::Type::Pattern, uint8_t(block - 1)); - } - break; - } - } - - return std::nullopt; + // A CPU/CMD at 1334. + if(grauw_index == 1334) { + return Event::Type::External; } - }; - struct TextGenerator { - static constexpr std::optional event(int grauw_index) { - // Capture various one-in-eight zones. - if(grauw_index < 72) { - return StandardGenerators::external_every_eight(grauw_index - 2); - } - if(grauw_index >= 166 && grauw_index < 228) { - return StandardGenerators::external_every_eight(grauw_index - 166); - } - if(grauw_index >= 1206 && grauw_index < 1332) { - return StandardGenerators::external_every_eight(grauw_index - 1206); - } - if(grauw_index == 1336) { - return Event::Type::External; - } - if(grauw_index >= 1346) { - return StandardGenerators::external_every_eight(grauw_index - 1346); - } - - // Elsewhere... - if(grauw_index >= 246) { - const int offset = grauw_index - 246; - const int block = offset / 48; - const int sub_block = offset % 48; - switch(sub_block) { - default: break; - case 0: return Event(Event::Type::Name, uint8_t(block)); - case 18: return (block & 1) ? Event::Type::External : Event(Event::Type::Colour, uint8_t(block >> 1)); - case 24: return Event(Event::Type::Pattern, uint8_t(block)); - } - } - - return std::nullopt; + // From 1344 to 1366: CPU/CMD slots every cycle divisible by 8. + if(grauw_index >= 1344 && grauw_index < 1366) { + return StandardGenerators::external_every_eight(grauw_index - 1344); } - }; - struct CharacterGenerator { - static constexpr std::optional event(int grauw_index) { - // Grab sprite events. + // Otherwise: nothing. + return std::nullopt; + } + }; + + template struct BitmapGenerator { + static constexpr std::optional event(const int grauw_index) { + if(!include_sprites) { + // Various standard zones of one-every-eight external slots. + if(grauw_index < 124) { + return StandardGenerators::external_every_eight(grauw_index + 2); + } + if(grauw_index > 1266) { + return StandardGenerators::external_every_eight(grauw_index - 1266); + } + } else { + // This records collection points for all data for selected sprites. + // There's only four of them (each site covering two sprites), + // so it's clearer just to be explicit. + // + // There's also a corresponding number of extra external slots to spell out. switch(grauw_index) { default: break; - case 1242: return Event(Event::Type::SpriteLocation, 0); - case 1306: return Event(Event::Type::SpriteLocation, 1); - case 6: return Event(Event::Type::SpriteLocation, 2); - case 70: return Event(Event::Type::SpriteLocation, 3); - case 1274: return Event(Event::Type::SpritePattern, 0); - case 1342: return Event(Event::Type::SpritePattern, 1); - case 38: return Event(Event::Type::SpritePattern, 2); - case 102: return Event(Event::Type::SpritePattern, 3); - case 1268: case 1334: case 32: case 96: return Event::Type::External; + case 1238: return Event(Event::Type::SpriteLocation, 0); + case 1302: return Event(Event::Type::SpriteLocation, 2); + case 2: return Event(Event::Type::SpriteLocation, 4); + case 66: return Event(Event::Type::SpriteLocation, 6); + case 1270: return Event(Event::Type::SpritePattern, 0); + case 1338: return Event(Event::Type::SpritePattern, 2); + case 34: return Event(Event::Type::SpritePattern, 4); + case 98: return Event(Event::Type::SpritePattern, 6); + case 1264: case 1330: case 28: case 92: + return Event::Type::External; } - - if(grauw_index >= 166 && grauw_index < 180) { - return StandardGenerators::external_every_eight(grauw_index - 166); - } - - if(grauw_index >= 182 && grauw_index < 1238) { - const int offset = grauw_index - 182; - const int block = offset / 32; - const int sub_block = offset & 31; - switch(sub_block) { - case 0: if(block > 0) return Event(Event::Type::Name, uint8_t(block - 1)); - case 6: if((sub_block & 3) != 3) return Event::Type::External; - case 12: if(block < 32) return Event(Event::Type::SpriteY, uint8_t(block)); - case 18: if(block > 0) return Event(Event::Type::Pattern, uint8_t(block - 1)); - case 24: if(block > 0) return Event(Event::Type::Colour, uint8_t(block - 1)); - } - } - - return std::nullopt; } - }; + + if(grauw_index >= 162 && grauw_index < 176) { + return StandardGenerators::external_every_eight(grauw_index - 162); + } + + // Everywhere else the pattern is: + // + // external or sprite y, external, data block + // + // Subject to caveats: + // + // 1) the first data block is just a dummy fetch with no side effects, + // so this emulator declines to record it; and + // 2) every fourth block, the second external is actually a refresh. + // + if(grauw_index >= 182 && grauw_index < 1238) { + const int offset = grauw_index - 182; + const int block = offset / 32; + const int sub_block = offset & 31; + + switch(sub_block) { + default: return std::nullopt; + case 0: + if(include_sprites) { + // Don't include the sprite post-amble (i.e. a spurious read with no side effects). + if(block < 32) { + return Event(Event::Type::SpriteY, uint8_t(block)); + } + } else { + return Event::Type::External; + } + case 6: + if((block & 3) != 3) { + return Event::Type::External; + } + break; + case 12: + if(block) { + return Event(Event::Type::Pattern, uint8_t(block - 1)); + } + break; + } + } + + return std::nullopt; + } + }; + + struct TextGenerator { + static constexpr std::optional event(const int grauw_index) { + // Capture various one-in-eight zones. + if(grauw_index < 72) { + return StandardGenerators::external_every_eight(grauw_index - 2); + } + if(grauw_index >= 166 && grauw_index < 228) { + return StandardGenerators::external_every_eight(grauw_index - 166); + } + if(grauw_index >= 1206 && grauw_index < 1332) { + return StandardGenerators::external_every_eight(grauw_index - 1206); + } + if(grauw_index == 1336) { + return Event::Type::External; + } + if(grauw_index >= 1346) { + return StandardGenerators::external_every_eight(grauw_index - 1346); + } + + // Elsewhere... + if(grauw_index >= 246) { + const int offset = grauw_index - 246; + const int block = offset / 48; + const int sub_block = offset % 48; + switch(sub_block) { + default: break; + case 0: return Event(Event::Type::Name, uint8_t(block)); + case 18: return (block & 1) ? Event::Type::External : Event(Event::Type::Colour, uint8_t(block >> 1)); + case 24: return Event(Event::Type::Pattern, uint8_t(block)); + } + } + + return std::nullopt; + } + }; + + struct CharacterGenerator { + static constexpr std::optional event(const int grauw_index) { + // Grab sprite events. + switch(grauw_index) { + default: break; + case 1242: return Event(Event::Type::SpriteLocation, 0); + case 1306: return Event(Event::Type::SpriteLocation, 1); + case 6: return Event(Event::Type::SpriteLocation, 2); + case 70: return Event(Event::Type::SpriteLocation, 3); + case 1274: return Event(Event::Type::SpritePattern, 0); + case 1342: return Event(Event::Type::SpritePattern, 1); + case 38: return Event(Event::Type::SpritePattern, 2); + case 102: return Event(Event::Type::SpritePattern, 3); + case 1268: case 1334: case 32: case 96: return Event::Type::External; + } + + if(grauw_index >= 166 && grauw_index < 180) { + return StandardGenerators::external_every_eight(grauw_index - 166); + } + + if(grauw_index >= 182 && grauw_index < 1238) { + const int offset = grauw_index - 182; + const int block = offset / 32; + const int sub_block = offset & 31; + switch(sub_block) { + case 0: if(block > 0) return Event(Event::Type::Name, uint8_t(block - 1)); + case 6: if((sub_block & 3) != 3) return Event::Type::External; + case 12: if(block < 32) return Event(Event::Type::SpriteY, uint8_t(block)); + case 18: if(block > 0) return Event(Event::Type::Pattern, uint8_t(block - 1)); + case 24: if(block > 0) return Event(Event::Type::Colour, uint8_t(block - 1)); + } + } + + return std::nullopt; + } + }; }; struct YamahaCommandState { @@ -314,7 +314,7 @@ struct YamahaCommandState { int minimum_command_column_ = 0; uint8_t command_latch_ = 0; - void update_command_step(int current_column) { + void update_command_step(const int current_column) { if(!command_) { next_command_step_ = CommandStep::None; return; @@ -354,7 +354,10 @@ struct YamahaCommandState { }; // Yamaha-specific storage. -template struct Storage>: public YamahaFetcher, public YamahaCommandState { +template +struct Storage>: + public YamahaFetcher, public YamahaCommandState +{ using AddressT = uint32_t; // The Yamaha's (optional in real hardware) additional 64kb of expansion RAM. @@ -410,7 +413,7 @@ template struct Storage void set(uint8_t value) { + template void set(const uint8_t value) { constexpr uint8_t mask = high ? (offset ? 0x3 : 0x1) : 0xff; constexpr int shift = high ? 8 : 0; v[offset] = (v[offset] & ~(mask << shift)) | ((value & mask) << shift); } - template void add(int amount) { + template void add(const int amount) { v[offset] += amount; if constexpr (offset == 1) { @@ -41,7 +41,7 @@ struct Vector { }; struct Colour { - void set(uint8_t value) { + void set(const uint8_t value) { colour = value; colour4bpp = uint8_t((value & 0xf) | (value << 4)); colour2bpp = uint8_t((colour4bpp & 0x33) | ((colour4bpp & 0x33) << 2)); @@ -131,7 +131,8 @@ struct Command { /// Current command parameters. CommandContext &context; ModeDescription &mode_description; - Command(CommandContext &context, ModeDescription &mode_description) : context(context), mode_description(mode_description) {} + Command(CommandContext &context, ModeDescription &mode_description) : + context(context), mode_description(mode_description) {} virtual ~Command() = default; /// @returns @c true if all output from this command is done; @c false otherwise. @@ -142,7 +143,7 @@ struct Command { virtual void advance() = 0; protected: - template void advance_axis(int offset = 1) { + template void advance_axis(const int offset = 1) { context.destination.add(context.arguments & (0x4 << axis) ? -offset : offset); if constexpr (include_source) { context.source.add(context.arguments & (0x4 << axis) ? -offset : offset); @@ -161,58 +162,58 @@ namespace Commands { /// * 88 cycles between every pixel plot; /// * plus an additional 32 cycles if a step along the minor axis is taken. struct Line: public Command { - public: - Line(CommandContext &context, ModeDescription &mode_description) : Command(context, mode_description) { - // context.destination = start position; - // context.size.v[0] = long side dots; - // context.size.v[1] = short side dots; - // context.arguments => direction +public: + Line(CommandContext &context, ModeDescription &mode_description) : Command(context, mode_description) { + // context.destination = start position; + // context.size.v[0] = long side dots; + // context.size.v[1] = short side dots; + // context.arguments => direction - position_ = context.size.v[1]; - numerator_ = position_ << 1; - denominator_ = context.size.v[0] << 1; + position_ = context.size.v[1]; + numerator_ = position_ << 1; + denominator_ = context.size.v[0] << 1; - cycles = 32; - access = AccessType::PlotPoint; + cycles = 32; + access = AccessType::PlotPoint; + } + + bool done() final { + return !context.size.v[0]; + } + + void advance() final { + --context.size.v[0]; + cycles = 88; + + // b0: 1 => long direction is y; + // 0 => long direction is x. + // + // b2: 1 => x direction is left; + // 0 => x direction is right. + // + // b3: 1 => y direction is up; + // 0 => y direction is down. + if(context.arguments & 0x1) { + advance_axis<1, false>(); + } else { + advance_axis<0, false>(); } - bool done() final { - return !context.size.v[0]; - } + position_ -= numerator_; + if(position_ < 0) { + position_ += denominator_; + cycles += 32; - void advance() final { - --context.size.v[0]; - cycles = 88; - - // b0: 1 => long direction is y; - // 0 => long direction is x. - // - // b2: 1 => x direction is left; - // 0 => x direction is right. - // - // b3: 1 => y direction is up; - // 0 => y direction is down. if(context.arguments & 0x1) { - advance_axis<1, false>(); - } else { advance_axis<0, false>(); - } - - position_ -= numerator_; - if(position_ < 0) { - position_ += denominator_; - cycles += 32; - - if(context.arguments & 0x1) { - advance_axis<0, false>(); - } else { - advance_axis<1, false>(); - } + } else { + advance_axis<1, false>(); } } + } - private: - int position_, numerator_, denominator_; +private: + int position_, numerator_, denominator_; }; // MARK: - Single pixel manipulation. @@ -221,88 +222,90 @@ struct Line: public Command { /// /// No timings are documented, so this'll output or input as quickly as possible. template struct Point: public Command { - public: - Point(CommandContext &context, ModeDescription &mode_description) : Command(context, mode_description) { - cycles = 0; // TODO. - access = is_read ? AccessType::ReadPoint : AccessType::PlotPoint; - } +public: + Point(CommandContext &context, ModeDescription &mode_description) : Command(context, mode_description) { + cycles = 0; // TODO. + access = is_read ? AccessType::ReadPoint : AccessType::PlotPoint; + } - bool done() final { - return done_; - } + bool done() final { + return done_; + } - void advance() final { - done_ = true; - } + void advance() final { + done_ = true; + } - private: - bool done_ = false; +private: + bool done_ = false; }; // MARK: - Rectangular base. /// Useful base class for anything that does logical work in a rectangle. template struct Rectangle: public Command { - public: - Rectangle(CommandContext &context, ModeDescription &mode_description) : Command(context, mode_description) { - if constexpr (include_source) { - start_x_[0] = context.source.v[0]; - } - start_x_[1] = context.destination.v[0]; - width_ = context.size.v[0]; +public: + Rectangle(CommandContext &context, ModeDescription &mode_description) : Command(context, mode_description) { + if constexpr (include_source) { + start_x_[0] = context.source.v[0]; + } + start_x_[1] = context.destination.v[0]; + width_ = context.size.v[0]; - if(!width_) { - // Width = 0 => maximal width for this mode. - // (aside: it's still unclear to me whether commands are - // automatically clipped to the display; I think so but - // don't want to spend any time on it until I'm certain) + if(!width_) { + // Width = 0 => maximal width for this mode. + // (aside: it's still unclear to me whether commands are + // automatically clipped to the display; I think so but + // don't want to spend any time on it until I'm certain) // context.size.v[0] = width_ = mode_description.width; + } + } + + /// Advances the current destination and, if @c include_source is @c true also the source; + /// @returns @c true if a new row was started; @c false otherwise. + bool advance_pixel() { + if constexpr (logical) { + advance_axis<0, include_source>(); + --context.size.v[0]; + + if(context.size.v[0]) { + return false; + } + } else { + advance_axis<0, include_source>(mode_description.pixels_per_byte); + context.size.v[0] -= mode_description.pixels_per_byte; + + if(context.size.v[0] & ~(mode_description.pixels_per_byte - 1)) { + return false; } } - /// Advances the current destination and, if @c include_source is @c true also the source; - /// @returns @c true if a new row was started; @c false otherwise. - bool advance_pixel() { - if constexpr (logical) { - advance_axis<0, include_source>(); - --context.size.v[0]; - - if(context.size.v[0]) { - return false; - } - } else { - advance_axis<0, include_source>(mode_description.pixels_per_byte); - context.size.v[0] -= mode_description.pixels_per_byte; - - if(context.size.v[0] & ~(mode_description.pixels_per_byte - 1)) { - return false; - } - } - - context.size.v[0] = width_; - if constexpr (include_source) { - context.source.v[0] = start_x_[0]; - } - context.destination.v[0] = start_x_[1]; - - advance_axis<1, include_source>(); - --context.size.v[1]; - - return true; + context.size.v[0] = width_; + if constexpr (include_source) { + context.source.v[0] = start_x_[0]; } + context.destination.v[0] = start_x_[1]; - bool done() final { - return !context.size.v[1] || !width_; - } + advance_axis<1, include_source>(); + --context.size.v[1]; - private: - int start_x_[2]{}, width_ = 0; + return true; + } + + bool done() final { + return !context.size.v[1] || !width_; + } + +private: + int start_x_[2]{}, width_ = 0; }; // MARK: - Rectangular moves to/from CPU. template struct MoveFromCPU: public Rectangle { - MoveFromCPU(CommandContext &context, ModeDescription &mode_description) : Rectangle(context, mode_description) { + MoveFromCPU(CommandContext &context, ModeDescription &mode_description) : + Rectangle(context, mode_description) + { Command::is_cpu_transfer = true; // This command is started with the first colour ready to transfer. diff --git a/Components/AY38910/AY38910.cpp b/Components/AY38910/AY38910.cpp index 434f04146..a48e67275 100644 --- a/Components/AY38910/AY38910.cpp +++ b/Components/AY38910/AY38910.cpp @@ -22,7 +22,11 @@ using namespace GI::AY38910; // programmatic volume 15, envelope level 29 as equal to programmatic 14, etc. template -AY38910SampleSource::AY38910SampleSource(Personality personality, Concurrency::AsyncTaskQueue &task_queue) : task_queue_(task_queue) { +AY38910SampleSource::AY38910SampleSource( + Personality personality, + Concurrency::AsyncTaskQueue &task_queue) + : task_queue_(task_queue) +{ // Don't use the low bit of the envelope position if this is an AY. envelope_position_mask_ |= personality == Personality::AY38910; @@ -82,7 +86,7 @@ AY38910SampleSource::AY38910SampleSource(Personality personality, Con } template -void AY38910SampleSource::set_sample_volume_range(std::int16_t range) { +void AY38910SampleSource::set_sample_volume_range(const std::int16_t range) { // Set up volume lookup table; the function below is based on a combination of the graph // from the YM's datasheet, showing a clear power curve, and fitting that to observed // values reported elsewhere. @@ -101,7 +105,14 @@ void AY38910SampleSource::set_sample_volume_range(std::int16_t range) } template -void AY38910SampleSource::set_output_mixing(float a_left, float b_left, float c_left, float a_right, float b_right, float c_right) { +void AY38910SampleSource::set_output_mixing( + const float a_left, + const float b_left, + const float c_left, + const float a_right, + const float b_right, + const float c_right +) { a_left_ = uint8_t(a_left * 255.0f); b_left_ = uint8_t(b_left * 255.0f); c_left_ = uint8_t(c_left * 255.0f); @@ -223,12 +234,12 @@ bool AY38910SampleSource::is_zero_level() const { // MARK: - Register manipulation template -void AY38910SampleSource::select_register(uint8_t r) { +void AY38910SampleSource::select_register(const uint8_t r) { selected_register_ = r; } template -void AY38910SampleSource::set_register_value(uint8_t value) { +void AY38910SampleSource::set_register_value(const uint8_t value) { // There are only 16 registers. if(selected_register_ > 15) return; @@ -298,7 +309,7 @@ void AY38910SampleSource::set_register_value(uint8_t value) { } template -uint8_t AY38910SampleSource::get_register_value() { +uint8_t AY38910SampleSource::get_register_value() const { // This table ensures that bits that aren't defined within the AY are returned as 0s // when read, conforming to CPC-sourced unit tests. const uint8_t register_masks[16] = { @@ -313,27 +324,27 @@ uint8_t AY38910SampleSource::get_register_value() { // MARK: - Port querying template -uint8_t AY38910SampleSource::get_port_output(bool port_b) { +uint8_t AY38910SampleSource::get_port_output(const bool port_b) const { return registers_[port_b ? 15 : 14]; } // MARK: - Bus handling template -void AY38910SampleSource::set_port_handler(PortHandler *handler) { +void AY38910SampleSource::set_port_handler(PortHandler *const handler) { port_handler_ = handler; set_port_output(true); set_port_output(false); } template -void AY38910SampleSource::set_data_input(uint8_t r) { +void AY38910SampleSource::set_data_input(const uint8_t r) { data_input_ = r; update_bus(); } template -void AY38910SampleSource::set_port_output(bool port_b) { +void AY38910SampleSource::set_port_output(const bool port_b) { // Per the data sheet: "each [IO] pin is provided with an on-chip pull-up resistor, // so that when in the "input" mode, all pins will read normally high". Therefore, // report programmer selection of input mode as creating an output of 0xff. @@ -344,7 +355,7 @@ void AY38910SampleSource::set_port_output(bool port_b) { } template -uint8_t AY38910SampleSource::get_data_output() { +uint8_t AY38910SampleSource::get_data_output() const { if(control_state_ == Read && selected_register_ >= 14 && selected_register_ < 16) { // Per http://cpctech.cpc-live.com/docs/psgnotes.htm if a port is defined as output then the // value returned to the CPU when reading it is the and of the output value and any input. @@ -361,7 +372,7 @@ uint8_t AY38910SampleSource::get_data_output() { } template -void AY38910SampleSource::set_control_lines(ControlLines control_lines) { +void AY38910SampleSource::set_control_lines(const ControlLines control_lines) { switch(int(control_lines)) { default: control_state_ = Inactive; break; @@ -377,7 +388,7 @@ void AY38910SampleSource::set_control_lines(ControlLines control_line } template -void AY38910SampleSource::set_reset(bool active) { +void AY38910SampleSource::set_reset(const bool active) { if(active == reset_) return; reset_ = active; diff --git a/Components/AY38910/AY38910.hpp b/Components/AY38910/AY38910.hpp index afb412e01..b9619687e 100644 --- a/Components/AY38910/AY38910.hpp +++ b/Components/AY38910/AY38910.hpp @@ -24,23 +24,23 @@ namespace GI::AY38910 { instead use AY38910.get_port_output. */ class PortHandler { - public: - /*! - Requests the current input on an AY port. +public: + /*! + Requests the current input on an AY port. - @param port_b @c true if the input being queried is Port B. @c false if it is Port A. - */ - virtual uint8_t get_port_input([[maybe_unused]] bool port_b) { - return 0xff; - } + @param port_b @c true if the input being queried is Port B. @c false if it is Port A. + */ + virtual uint8_t get_port_input([[maybe_unused]] bool port_b) { + return 0xff; + } - /*! - Sets the current output on an AY port. + /*! + Sets the current output on an AY port. - @param port_b @c true if the output being posted is Port B. @c false if it is Port A. - @param value the value now being output. - */ - virtual void set_port_output([[maybe_unused]] bool port_b, [[maybe_unused]] uint8_t value) {} + @param port_b @c true if the output being posted is Port B. @c false if it is Port A. + @param value the value now being output. + */ + virtual void set_port_output([[maybe_unused]] bool port_b, [[maybe_unused]] uint8_t value) {} }; /*! @@ -67,110 +67,110 @@ enum class Personality { This AY has an attached mono or stereo mixer. */ template class AY38910SampleSource { - public: - /// Creates a new AY38910. - AY38910SampleSource(Personality, Concurrency::AsyncTaskQueue &); - AY38910SampleSource(const AY38910SampleSource &) = delete; +public: + /// Creates a new AY38910. + AY38910SampleSource(Personality, Concurrency::AsyncTaskQueue &); + AY38910SampleSource(const AY38910SampleSource &) = delete; - /// Sets the value the AY would read from its data lines if it were not outputting. - void set_data_input(uint8_t r); + /// Sets the value the AY would read from its data lines if it were not outputting. + void set_data_input(uint8_t r); - /// Gets the value that would appear on the data lines if only the AY is outputting. - uint8_t get_data_output(); + /// Gets the value that would appear on the data lines if only the AY is outputting. + uint8_t get_data_output() const; - /// Sets the current control line state, as a bit field. - void set_control_lines(ControlLines control_lines); + /// Sets the current control line state, as a bit field. + void set_control_lines(ControlLines control_lines); - /// Strobes the reset line. - void reset(); + /// Strobes the reset line. + void reset(); - /// Sets the current value of the reset line. - void set_reset(bool reset); + /// Sets the current value of the reset line. + void set_reset(bool reset); - /*! - Gets the value that would appear on the requested interface port if it were in output mode. - @parameter port_b @c true to get the value for Port B, @c false to get the value for Port A. - */ - uint8_t get_port_output(bool port_b); + /*! + Gets the value that would appear on the requested interface port if it were in output mode. + @parameter port_b @c true to get the value for Port B, @c false to get the value for Port A. + */ + uint8_t get_port_output(bool port_b) const; - /*! - Sets the port handler, which will receive a call every time the AY either wants to sample - input or else declare new output. As a convenience, current port output can be obtained - without installing a port handler via get_port_output. - */ - void set_port_handler(PortHandler *); + /*! + Sets the port handler, which will receive a call every time the AY either wants to sample + input or else declare new output. As a convenience, current port output can be obtained + without installing a port handler via get_port_output. + */ + void set_port_handler(PortHandler *); - /*! - Enables or disables stereo output; if stereo output is enabled then also sets the weight of each of the AY's - channels in each of the output channels. + /*! + Enables or disables stereo output; if stereo output is enabled then also sets the weight of each of the AY's + channels in each of the output channels. - If a_left_ = b_left = c_left = a_right = b_right = c_right = 1.0 then you'll get output that's effectively mono. + If a_left_ = b_left = c_left = a_right = b_right = c_right = 1.0 then you'll get output that's effectively mono. - a_left = 0.0, a_right = 1.0 will make A full volume on the right output, and silent on the left. + a_left = 0.0, a_right = 1.0 will make A full volume on the right output, and silent on the left. - a_left = 0.5, a_right = 0.5 will make A half volume on both outputs. - */ - void set_output_mixing(float a_left, float b_left, float c_left, float a_right = 1.0, float b_right = 1.0, float c_right = 1.0); + a_left = 0.5, a_right = 0.5 will make A half volume on both outputs. + */ + void set_output_mixing(float a_left, float b_left, float c_left, float a_right = 1.0, float b_right = 1.0, float c_right = 1.0); - // Sample generation. - typename Outputs::Speaker::SampleT::type level() const; - void advance(); - bool is_zero_level() const; - void set_sample_volume_range(std::int16_t range); + // Sample generation. + typename Outputs::Speaker::SampleT::type level() const; + void advance(); + bool is_zero_level() const; + void set_sample_volume_range(std::int16_t range); - private: - Concurrency::AsyncTaskQueue &task_queue_; +private: + Concurrency::AsyncTaskQueue &task_queue_; - bool reset_ = false; + bool reset_ = false; - int selected_register_ = 0; - uint8_t registers_[16]{}; - uint8_t output_registers_[16]{}; + int selected_register_ = 0; + uint8_t registers_[16]{}; + uint8_t output_registers_[16]{}; - int tone_periods_[3]{}; - int tone_counters_[3]{}; - int tone_outputs_[3]{}; + int tone_periods_[3]{}; + int tone_counters_[3]{}; + int tone_outputs_[3]{}; - int noise_period_ = 0; - int noise_counter_ = 0; - int noise_shift_register_ = 0xffff; - int noise_output_ = 0; + int noise_period_ = 0; + int noise_counter_ = 0; + int noise_shift_register_ = 0xffff; + int noise_output_ = 0; - int envelope_period_ = 0; - int envelope_divider_ = 0; - int envelope_position_ = 0, envelope_position_mask_ = 0; - int envelope_shapes_[16][64]; - int envelope_overflow_masks_[16]; + int envelope_period_ = 0; + int envelope_divider_ = 0; + int envelope_position_ = 0, envelope_position_mask_ = 0; + int envelope_shapes_[16][64]; + int envelope_overflow_masks_[16]; - int volumes_[32]; + int volumes_[32]; - enum ControlState { - Inactive, - LatchAddress, - Read, - Write - } control_state_; + enum ControlState { + Inactive, + LatchAddress, + Read, + Write + } control_state_; - void select_register(uint8_t r); - void set_register_value(uint8_t value); - uint8_t get_register_value(); + void select_register(uint8_t r); + void set_register_value(uint8_t value); + uint8_t get_register_value() const; - uint8_t data_input_, data_output_; + uint8_t data_input_, data_output_; - typename Outputs::Speaker::SampleT::type output_volume_; + typename Outputs::Speaker::SampleT::type output_volume_; - void update_bus(); - PortHandler *port_handler_ = nullptr; - void set_port_output(bool port_b); + void update_bus(); + PortHandler *port_handler_ = nullptr; + void set_port_output(bool port_b); - void evaluate_output_volume(); + void evaluate_output_volume(); - // Output mixing control. - uint8_t a_left_ = 255, a_right_ = 255; - uint8_t b_left_ = 255, b_right_ = 255; - uint8_t c_left_ = 255, c_right_ = 255; + // Output mixing control. + uint8_t a_left_ = 255, a_right_ = 255; + uint8_t b_left_ = 255, b_right_ = 255; + uint8_t c_left_ = 255, c_right_ = 255; - friend struct State; + friend struct State; }; /// Defines a default AY to be the sample source with a master divider of 4; @@ -193,7 +193,9 @@ template struct AY38910: */ struct Utility { template static void write(AY &ay, bool is_data_write, uint8_t data) { - ay.set_control_lines(GI::AY38910::ControlLines(GI::AY38910::BDIR | GI::AY38910::BC2 | (is_data_write ? 0 : GI::AY38910::BC1))); + ay.set_control_lines( + GI::AY38910::ControlLines(GI::AY38910::BDIR | GI::AY38910::BC2 | (is_data_write ? 0 : GI::AY38910::BC1)) + ); ay.set_data_input(data); ay.set_control_lines(GI::AY38910::ControlLines(0)); } diff --git a/Components/AppleClock/AppleClock.hpp b/Components/AppleClock/AppleClock.hpp index ab2ec04c4..bc3b5f8b0 100644 --- a/Components/AppleClock/AppleClock.hpp +++ b/Components/AppleClock/AppleClock.hpp @@ -20,273 +20,272 @@ namespace Apple::Clock { will also signal interrupts — this is just the storage and time counting. */ class ClockStorage { - public: - /*! - Advances the clock by 1 second. +public: + /*! + Advances the clock by 1 second. - The caller should also signal an interrupt if applicable. + The caller should also signal an interrupt if applicable. + */ + void update() { + for(size_t c = 0; c < 4; ++c) { + ++seconds_[c]; + if(seconds_[c]) break; + } + } + + /*! + Sets the current [P/B]RAM contents. + */ + template void set_data(const CollectionT &collection) { + set_data(collection.begin(), collection.end()); + } + + template void set_data(IteratorT begin, const IteratorT end) { + size_t c = 0; + while(begin != end && c < 256) { + data_[c] = *begin; + ++begin; + ++c; + } + } + +protected: + static constexpr uint16_t NoResult = 0x100; + static constexpr uint16_t DidComplete = 0x101; + uint16_t perform(const uint8_t command) { + /* + Documented commands: + + z0000001 Seconds register 0 (lowest order byte) + z0000101 Seconds register 1 + z0001001 Seconds register 2 + z0001101 Seconds register 3 + 00110001 Test register (write only) + 00110101 Write-protect register (write only) + z010aa01 RAM addresses 0x10 - 0x13 + z1aaaa01 RAM addresses 0x00 – 0x0f + + z0111abc, followed by 0defgh00 + RAM address abcdefgh + + z = 1 => a read; z = 0 => a write. + + The top bit of the write-protect register enables (0) or disables (1) + writes to other locations. + + All the documentation says about the test register is to set the top + two bits to 0 for normal operation. Abnormal operation is undefined. */ - void update() { - for(size_t c = 0; c < 4; ++c) { - ++seconds_[c]; - if(seconds_[c]) break; - } - } + switch(phase_) { + case Phase::Command: + // Decode an address. + switch(command & 0x70) { + default: + if(command & 0x40) { + // RAM addresses 0x00 – 0x0f. + address_ = (command >> 2) & 0xf; + } else return DidComplete; // Unrecognised. + break; - /*! - Sets the current [P/B]RAM contents. - */ - template void set_data(const CollectionT &collection) { - set_data(collection.begin(), collection.end()); - } - - template void set_data(IteratorT begin, const IteratorT end) { - size_t c = 0; - while(begin != end && c < 256) { - data_[c] = *begin; - ++begin; - ++c; - } - } - - protected: - static constexpr uint16_t NoResult = 0x100; - static constexpr uint16_t DidComplete = 0x101; - uint16_t perform(uint8_t command) { - /* - Documented commands: - - z0000001 Seconds register 0 (lowest order byte) - z0000101 Seconds register 1 - z0001001 Seconds register 2 - z0001101 Seconds register 3 - 00110001 Test register (write only) - 00110101 Write-protect register (write only) - z010aa01 RAM addresses 0x10 - 0x13 - z1aaaa01 RAM addresses 0x00 – 0x0f - - z0111abc, followed by 0defgh00 - RAM address abcdefgh - - z = 1 => a read; z = 0 => a write. - - The top bit of the write-protect register enables (0) or disables (1) - writes to other locations. - - All the documentation says about the test register is to set the top - two bits to 0 for normal operation. Abnormal operation is undefined. - */ - switch(phase_) { - case Phase::Command: - // Decode an address. - switch(command & 0x70) { - default: - if(command & 0x40) { - // RAM addresses 0x00 – 0x0f. - address_ = (command >> 2) & 0xf; - } else return DidComplete; // Unrecognised. - break; - - case 0x00: - // A time access. - address_ = SecondsBuffer + ((command >> 2)&3); - break; - case 0x30: - // Either a register access or an extended instruction. - if(command & 0x08) { - address_ = unsigned((command & 0x7) << 5); - phase_ = (command & 0x80) ? Phase::SecondAddressByteRead : Phase::SecondAddressByteWrite; - return NoResult; - } else { - address_ = (command & 4) ? RegisterWriteProtect : RegisterTest; - } - break; - case 0x20: - // RAM addresses 0x10 – 0x13. - address_ = 0x10 + ((command >> 2) & 0x3); - break; - } - - // If this is a read, return a result; otherwise prepare to write. - if(command & 0x80) { - // The two registers are write-only. - if(address_ == RegisterTest || address_ == RegisterWriteProtect) { - return DidComplete; - } - return (address_ >= SecondsBuffer) ? seconds_[address_ & 0xff] : data_[address_]; - } - phase_ = Phase::WriteData; - return NoResult; - - case Phase::SecondAddressByteRead: - case Phase::SecondAddressByteWrite: - if(command & 0x83) { - return DidComplete; - } - address_ |= command >> 2; - - if(phase_ == Phase::SecondAddressByteRead) { - phase_ = Phase::Command; - return data_[address_]; // Only RAM accesses can get this far. - } else { - phase_ = Phase::WriteData; - } - return NoResult; - - case Phase::WriteData: - // First test: is this to the write-protect register? - if(address_ == RegisterWriteProtect) { - write_protect_ = command; - return DidComplete; - } - - if(address_ == RegisterTest) { - // No documentation here. - return DidComplete; - } - - // No other writing is permitted if the write protect - // register won't allow it. - if(!(write_protect_ & 0x80)) { - if(address_ >= SecondsBuffer) { - seconds_[address_ & 0xff] = command; + case 0x00: + // A time access. + address_ = SecondsBuffer + ((command >> 2)&3); + break; + case 0x30: + // Either a register access or an extended instruction. + if(command & 0x08) { + address_ = unsigned((command & 0x7) << 5); + phase_ = (command & 0x80) ? Phase::SecondAddressByteRead : Phase::SecondAddressByteWrite; + return NoResult; } else { - data_[address_] = command; + address_ = (command & 4) ? RegisterWriteProtect : RegisterTest; } + break; + case 0x20: + // RAM addresses 0x10 – 0x13. + address_ = 0x10 + ((command >> 2) & 0x3); + break; + } + + // If this is a read, return a result; otherwise prepare to write. + if(command & 0x80) { + // The two registers are write-only. + if(address_ == RegisterTest || address_ == RegisterWriteProtect) { + return DidComplete; } - - phase_ = Phase::Command; - return DidComplete; - } - + return (address_ >= SecondsBuffer) ? seconds_[address_ & 0xff] : data_[address_]; + } + phase_ = Phase::WriteData; return NoResult; + + case Phase::SecondAddressByteRead: + case Phase::SecondAddressByteWrite: + if(command & 0x83) { + return DidComplete; + } + address_ |= command >> 2; + + if(phase_ == Phase::SecondAddressByteRead) { + phase_ = Phase::Command; + return data_[address_]; // Only RAM accesses can get this far. + } else { + phase_ = Phase::WriteData; + } + return NoResult; + + case Phase::WriteData: + // First test: is this to the write-protect register? + if(address_ == RegisterWriteProtect) { + write_protect_ = command; + return DidComplete; + } + + if(address_ == RegisterTest) { + // No documentation here. + return DidComplete; + } + + // No other writing is permitted if the write protect + // register won't allow it. + if(!(write_protect_ & 0x80)) { + if(address_ >= SecondsBuffer) { + seconds_[address_ & 0xff] = command; + } else { + data_[address_] = command; + } + } + + phase_ = Phase::Command; + return DidComplete; } + return NoResult; + } - private: - std::array data_{0xff}; - std::array seconds_{}; - uint8_t write_protect_ = 0; - unsigned int address_ = 0; - static constexpr int SecondsBuffer = 0x100; - static constexpr int RegisterTest = 0x200; - static constexpr int RegisterWriteProtect = 0x201; +private: + std::array data_{0xff}; + std::array seconds_{}; + uint8_t write_protect_ = 0; + unsigned int address_ = 0; - enum class Phase { - Command, - SecondAddressByteRead, - SecondAddressByteWrite, - WriteData - }; - Phase phase_ = Phase::Command; + static constexpr int SecondsBuffer = 0x100; + static constexpr int RegisterTest = 0x200; + static constexpr int RegisterWriteProtect = 0x201; + enum class Phase { + Command, + SecondAddressByteRead, + SecondAddressByteWrite, + WriteData + }; + Phase phase_ = Phase::Command; }; /*! Provides the serial interface implemented by the Macintosh. */ class SerialClock: public ClockStorage { - public: - /*! - Sets the current clock and data inputs to the clock. - */ - void set_input(bool clock, bool data) { - // The data line is valid when the clock transitions to level 0. - if(clock && !previous_clock_) { - // Shift into the command_ register, no matter what. - command_ = uint16_t((command_ << 1) | (data ? 1 : 0)); - result_ <<= 1; +public: + /*! + Sets the current clock and data inputs to the clock. + */ + void set_input(const bool clock, const bool data) { + // The data line is valid when the clock transitions to level 0. + if(clock && !previous_clock_) { + // Shift into the command_ register, no matter what. + command_ = uint16_t((command_ << 1) | (data ? 1 : 0)); + result_ <<= 1; - // Increment phase. - ++phase_; + // Increment phase. + ++phase_; - // If a whole byte has been collected, push it onwards. - if(!(phase_&7)) { - // Begin pessimistically. - const auto effect = perform(uint8_t(command_)); + // If a whole byte has been collected, push it onwards. + if(!(phase_&7)) { + // Begin pessimistically. + const auto effect = perform(uint8_t(command_)); - switch(effect) { - case ClockStorage::NoResult: - break; - default: - result_ = uint8_t(effect); - break; - case ClockStorage::DidComplete: - abort(); - break; - } + switch(effect) { + case ClockStorage::NoResult: + break; + default: + result_ = uint8_t(effect); + break; + case ClockStorage::DidComplete: + abort(); + break; } } - - previous_clock_ = clock; } - /*! - Reads the current data output level from the clock. - */ - bool get_data() { - return !!(result_ & 0x80); - } + previous_clock_ = clock; + } - /*! - Announces that a serial command has been aborted. - */ - void abort() { - result_ = 0; - phase_ = 0; - command_ = 0; - } + /*! + Reads the current data output level from the clock. + */ + bool get_data() const { + return !!(result_ & 0x80); + } - private: - int phase_ = 0; - uint16_t command_; - uint8_t result_ = 0; + /*! + Announces that a serial command has been aborted. + */ + void abort() { + result_ = 0; + phase_ = 0; + command_ = 0; + } - bool previous_clock_ = false; +private: + int phase_ = 0; + uint16_t command_; + uint8_t result_ = 0; + + bool previous_clock_ = false; }; /*! Provides the parallel interface implemented by the IIgs. */ class ParallelClock: public ClockStorage { - public: - void set_control(uint8_t control) { - if(!(control&0x80)) return; +public: + void set_control(const uint8_t control) { + if(!(control&0x80)) return; - if(control & 0x40) { - // Read from the RTC. - // A no-op for now. - } else { - // Write to the RTC. Which in this implementation also sets up a future read. - const auto result = perform(data_); - if(result < 0x100) { - data_ = uint8_t(result); - } + if(control & 0x40) { + // Read from the RTC. + // A no-op for now. + } else { + // Write to the RTC. Which in this implementation also sets up a future read. + const auto result = perform(data_); + if(result < 0x100) { + data_ = uint8_t(result); } - - // MAGIC! The transaction took 0 seconds. - // TODO: no magic. - control_ = control & 0x7f; - - // Bit 5 is also meant to be 1 or 0 to indicate the final byte. } - uint8_t get_control() { - return control_; - } + // MAGIC! The transaction took 0 seconds. + // TODO: no magic. + control_ = control & 0x7f; - void set_data(uint8_t data) { - data_ = data; - } + // Bit 5 is also meant to be 1 or 0 to indicate the final byte. + } - uint8_t get_data() { - return data_; - } + uint8_t get_control() const { + return control_; + } - private: - uint8_t data_; - uint8_t control_; + void set_data(const uint8_t data) { + data_ = data; + } + + uint8_t get_data() const { + return data_; + } + +private: + uint8_t data_; + uint8_t control_; }; } diff --git a/Components/AudioToggle/AudioToggle.cpp b/Components/AudioToggle/AudioToggle.cpp index 77c329972..f3d74c540 100644 --- a/Components/AudioToggle/AudioToggle.cpp +++ b/Components/AudioToggle/AudioToggle.cpp @@ -15,12 +15,12 @@ using namespace Audio; Audio::Toggle::Toggle(Concurrency::AsyncTaskQueue &audio_queue) : audio_queue_(audio_queue) {} -void Toggle::set_sample_volume_range(std::int16_t range) { +void Toggle::set_sample_volume_range(const std::int16_t range) { volume_ = range; level_ = level_active_ ? volume_ : 0; } -void Toggle::set_output(bool enabled) { +void Toggle::set_output(const bool enabled) { if(is_enabled_ == enabled) return; is_enabled_ = enabled; audio_queue_.enqueue([this, enabled] { diff --git a/Components/AudioToggle/AudioToggle.hpp b/Components/AudioToggle/AudioToggle.hpp index 06312225c..4146c66de 100644 --- a/Components/AudioToggle/AudioToggle.hpp +++ b/Components/AudioToggle/AudioToggle.hpp @@ -17,29 +17,29 @@ namespace Audio { Provides a sample source that can programmatically be set to one of two values. */ class Toggle: public Outputs::Speaker::BufferSource { - public: - Toggle(Concurrency::AsyncTaskQueue &audio_queue); +public: + Toggle(Concurrency::AsyncTaskQueue &audio_queue); - template - void apply_samples(std::size_t number_of_samples, Outputs::Speaker::MonoSample *target) { - Outputs::Speaker::fill(target, target + number_of_samples, level_); - } - void set_sample_volume_range(std::int16_t range); - bool is_zero_level() const { - return !level_; - } + template + void apply_samples(const std::size_t number_of_samples, Outputs::Speaker::MonoSample *const target) { + Outputs::Speaker::fill(target, target + number_of_samples, level_); + } + void set_sample_volume_range(const std::int16_t range); + bool is_zero_level() const { + return !level_; + } - void set_output(bool enabled); - bool get_output() const; + void set_output(bool enabled); + bool get_output() const; - private: - // Accessed on the calling thread. - bool is_enabled_ = false; - Concurrency::AsyncTaskQueue &audio_queue_; +private: + // Accessed on the calling thread. + bool is_enabled_ = false; + Concurrency::AsyncTaskQueue &audio_queue_; - // Accessed on the audio thread. - int16_t level_ = 0, volume_ = 0; - bool level_active_ = false; + // Accessed on the audio thread. + int16_t level_ = 0, volume_ = 0; + bool level_active_ = false; }; } diff --git a/Components/DiskII/DiskII.cpp b/Components/DiskII/DiskII.cpp index 19d3febe7..81d235408 100644 --- a/Components/DiskII/DiskII.cpp +++ b/Components/DiskII/DiskII.cpp @@ -19,7 +19,7 @@ namespace { const uint8_t input_flux = 0x1; } -DiskII::DiskII(int clock_rate) : +DiskII::DiskII(const int clock_rate) : clock_rate_(clock_rate), inputs_(input_command), drives_{ @@ -38,7 +38,7 @@ DiskII::DiskII(int clock_rate) : drives_[active_drive_].set_event_delegate(this); } -void DiskII::set_control(Control control, bool on) { +void DiskII::set_control(const Control control, const bool on) { int previous_stepper_mask = stepper_mask_; switch(control) { case Control::P0: stepper_mask_ = (stepper_mask_ & 0xe) | (on ? 0x1 : 0x0); break; @@ -76,7 +76,7 @@ void DiskII::set_control(Control control, bool on) { } } -void DiskII::select_drive(int drive) { +void DiskII::select_drive(const int drive) { if((drive&1) == active_drive_) return; drives_[active_drive_].set_event_delegate(this); @@ -152,7 +152,7 @@ void DiskII::run_for(const Cycles cycles) { } void DiskII::decide_clocking_preference() { - ClockingHint::Preference prior_preference = clocking_preference_; + const ClockingHint::Preference prior_preference = clocking_preference_; // If in read mode, clocking is either: // @@ -183,7 +183,7 @@ void DiskII::decide_clocking_preference() { update_clocking_observer(); } -bool DiskII::is_write_protected() { +bool DiskII::is_write_protected() const { return (stepper_mask_ & 2) || drives_[active_drive_].get_is_read_only(); } diff --git a/Components/DiskII/DiskII.hpp b/Components/DiskII/DiskII.hpp index 68df92c26..17851b62a 100644 --- a/Components/DiskII/DiskII.hpp +++ b/Components/DiskII/DiskII.hpp @@ -29,99 +29,99 @@ class DiskII : public Storage::Disk::Drive::EventDelegate, public ClockingHint::Source, public ClockingHint::Observer { - public: - DiskII(int clock_rate); +public: + DiskII(int clock_rate); - /// Sets the current external value of the data bus. - void set_data_input(uint8_t input); + /// Sets the current external value of the data bus. + void set_data_input(uint8_t input); - /*! - Submits an access to address @c address. + /*! + Submits an access to address @c address. - @returns The 8-bit value loaded to the data bus by the DiskII if any; - @c DidNotLoad otherwise. - */ - int read_address(int address); + @returns The 8-bit value loaded to the data bus by the DiskII if any; + @c DidNotLoad otherwise. + */ + int read_address(int address); - /*! - The value returned by @c read_address if accessing that address - didn't cause the disk II to place anything onto the bus. - */ - static constexpr int DidNotLoad = -1; + /*! + The value returned by @c read_address if accessing that address + didn't cause the disk II to place anything onto the bus. + */ + static constexpr int DidNotLoad = -1; - /// Advances the controller by @c cycles. - void run_for(const Cycles cycles); + /// Advances the controller by @c cycles. + void run_for(const Cycles cycles); - /*! - Supplies the image of the state machine (i.e. P6) ROM, - which dictates how the Disk II will respond to input. + /*! + Supplies the image of the state machine (i.e. P6) ROM, + which dictates how the Disk II will respond to input. - To reduce processing costs, some assumptions are made by - the implementation as to the content of this ROM. - Including: + To reduce processing costs, some assumptions are made by + the implementation as to the content of this ROM. + Including: - * If Q6 is set and Q7 is reset, the controller is testing - for write protect. If and when the shift register has - become full with the state of the write protect value, - no further processing is required. + * If Q6 is set and Q7 is reset, the controller is testing + for write protect. If and when the shift register has + become full with the state of the write protect value, + no further processing is required. - * If both Q6 and Q7 are reset, the drive motor is disabled, - and the shift register is all zeroes, no further processing - is required. - */ - void set_state_machine(const std::vector &); + * If both Q6 and Q7 are reset, the drive motor is disabled, + and the shift register is all zeroes, no further processing + is required. + */ + void set_state_machine(const std::vector &); - /// Inserts @c disk into the drive @c drive. - void set_disk(const std::shared_ptr &disk, int drive); + /// Inserts @c disk into the drive @c drive. + void set_disk(const std::shared_ptr &disk, int drive); - // As per Sleeper. - ClockingHint::Preference preferred_clocking() const final; + // As per Sleeper. + ClockingHint::Preference preferred_clocking() const final; - // The Disk II functions as a potential target for @c Activity::Sources. - void set_activity_observer(Activity::Observer *observer); + // The Disk II functions as a potential target for @c Activity::Sources. + void set_activity_observer(Activity::Observer *observer); - // Returns the Storage::Disk::Drive in use for drive @c index. - // *NOT FOR HARDWARE EMULATION USAGE*. - Storage::Disk::Drive &get_drive(int index); + // Returns the Storage::Disk::Drive in use for drive @c index. + // *NOT FOR HARDWARE EMULATION USAGE*. + Storage::Disk::Drive &get_drive(int index); - private: - enum class Control { - P0, P1, P2, P3, - Motor, - }; - enum class Mode { - Read, Write - }; - void set_control(Control control, bool on); - void set_mode(Mode mode); - void select_drive(int drive); +private: + enum class Control { + P0, P1, P2, P3, + Motor, + }; + enum class Mode { + Read, Write + }; + void set_control(Control control, bool on); + void set_mode(Mode mode); + void select_drive(int drive); - uint8_t trigger_address(int address, uint8_t value); - void process_event(const Storage::Disk::Drive::Event &event) final; - void set_component_prefers_clocking(ClockingHint::Source *component, ClockingHint::Preference preference) final; + uint8_t trigger_address(int address, uint8_t value); + void process_event(const Storage::Disk::Drive::Event &event) final; + void set_component_prefers_clocking(ClockingHint::Source *component, ClockingHint::Preference preference) final; - const Cycles::IntType clock_rate_ = 0; + const Cycles::IntType clock_rate_ = 0; - uint8_t state_ = 0; - uint8_t inputs_ = 0; - uint8_t shift_register_ = 0; + uint8_t state_ = 0; + uint8_t inputs_ = 0; + uint8_t shift_register_ = 0; - int stepper_mask_ = 0; - int stepper_position_ = 0; - Cycles::IntType motor_off_time_ = -1; + int stepper_mask_ = 0; + int stepper_position_ = 0; + Cycles::IntType motor_off_time_ = -1; - bool is_write_protected(); - std::array state_machine_; - Storage::Disk::Drive drives_[2]; - bool drive_is_sleeping_[2]; - int active_drive_ = 0; - bool motor_is_enabled_ = false; + bool is_write_protected() const; + std::array state_machine_; + Storage::Disk::Drive drives_[2]; + bool drive_is_sleeping_[2]; + int active_drive_ = 0; + bool motor_is_enabled_ = false; - void decide_clocking_preference(); - ClockingHint::Preference clocking_preference_ = ClockingHint::Preference::RealTime; + void decide_clocking_preference(); + ClockingHint::Preference clocking_preference_ = ClockingHint::Preference::RealTime; - uint8_t data_input_ = 0; - int flux_duration_ = 0; + uint8_t data_input_ = 0; + int flux_duration_ = 0; }; } diff --git a/Components/DiskII/DiskIIDrive.cpp b/Components/DiskII/DiskIIDrive.cpp index a27bbf43f..fcee9ddcb 100644 --- a/Components/DiskII/DiskIIDrive.cpp +++ b/Components/DiskII/DiskIIDrive.cpp @@ -10,16 +10,16 @@ using namespace Apple::Disk; -DiskIIDrive::DiskIIDrive(int input_clock_rate) : +DiskIIDrive::DiskIIDrive(const int input_clock_rate) : IWMDrive(input_clock_rate, 1) { Drive::set_rotation_speed(300.0f); } -void DiskIIDrive::set_enabled(bool enabled) { +void DiskIIDrive::set_enabled(const bool enabled) { set_motor_on(enabled); } -void DiskIIDrive::set_control_lines(int lines) { +void DiskIIDrive::set_control_lines(const int lines) { // If the stepper magnet selections have changed, and any is on, see how // that moves the head. if(lines ^ stepper_mask_ && lines) { diff --git a/Components/DiskII/DiskIIDrive.hpp b/Components/DiskII/DiskIIDrive.hpp index 9de3523d4..18220988f 100644 --- a/Components/DiskII/DiskIIDrive.hpp +++ b/Components/DiskII/DiskIIDrive.hpp @@ -13,16 +13,16 @@ namespace Apple::Disk { class DiskIIDrive: public IWMDrive { - public: - DiskIIDrive(int input_clock_rate); +public: + DiskIIDrive(int input_clock_rate); - private: - void set_enabled(bool) final; - void set_control_lines(int) final; - bool read() final; +private: + void set_enabled(bool) final; + void set_control_lines(int) final; + bool read() final; - int stepper_mask_ = 0; - int stepper_position_ = 0; + int stepper_mask_ = 0; + int stepper_position_ = 0; }; } diff --git a/Components/DiskII/IWM.cpp b/Components/DiskII/IWM.cpp index 8a047eb03..5ccb71f29 100644 --- a/Components/DiskII/IWM.cpp +++ b/Components/DiskII/IWM.cpp @@ -21,7 +21,8 @@ namespace { constexpr int DRIVESEL = 1 << 5; /* This means drive select, like on the original Disk II. */ constexpr int Q6 = 1 << 6; constexpr int Q7 = 1 << 7; - constexpr int SEL = 1 << 8; /* This is an additional input, not available on a Disk II, with a confusingly-similar name to SELECT but a distinct purpose. */ + constexpr int SEL = 1 << 8; /* This is an additional input, not available on a Disk II, with a + confusingly-similar name to SELECT but a distinct purpose. */ Log::Logger logger; } @@ -31,7 +32,7 @@ IWM::IWM(int clock_rate) : // MARK: - Bus accessors -uint8_t IWM::read(int address) { +uint8_t IWM::read(const int address) { access(address); // Per Inside Macintosh: @@ -103,7 +104,7 @@ uint8_t IWM::read(int address) { return 0xff; } -void IWM::write(int address, uint8_t input) { +void IWM::write(const int address, const uint8_t input) { access(address); switch(state_ & (Q6 | Q7 | ENABLE)) { @@ -205,7 +206,7 @@ void IWM::access(int address) { } } -void IWM::set_select(bool enabled) { +void IWM::set_select(const bool enabled) { // Store SEL as an extra state bit. if(enabled) state_ |= SEL; else state_ &= ~SEL; @@ -367,7 +368,7 @@ void IWM::process_event(const Storage::Disk::Drive::Event &event) { } } -void IWM::propose_shift(uint8_t bit) { +void IWM::propose_shift(const uint8_t bit) { // TODO: synchronous mode. // logger.info().append("Shifting at %d", cycles_since_shift_.as()); @@ -397,7 +398,7 @@ void IWM::propose_shift(uint8_t bit) { cycles_since_shift_ -= bit_length_; } -void IWM::set_drive(int slot, IWMDrive *drive) { +void IWM::set_drive(const int slot, IWMDrive *const drive) { drives_[slot] = drive; if(drive) { drive->set_event_delegate(this); @@ -407,7 +408,10 @@ void IWM::set_drive(int slot, IWMDrive *drive) { } } -void IWM::set_component_prefers_clocking(ClockingHint::Source *component, ClockingHint::Preference clocking) { +void IWM::set_component_prefers_clocking( + ClockingHint::Source *const component, + const ClockingHint::Preference clocking +) { const bool is_rotating = clocking != ClockingHint::Preference::None; if(drives_[0] && component == static_cast(drives_[0])) { @@ -417,7 +421,7 @@ void IWM::set_component_prefers_clocking(ClockingHint::Source *component, Clocki } } -void IWM::set_activity_observer(Activity::Observer *observer) { +void IWM::set_activity_observer(Activity::Observer *const observer) { if(drives_[0]) drives_[0]->set_activity_observer(observer, "Internal Floppy", true); if(drives_[1]) drives_[1]->set_activity_observer(observer, "External Floppy", true); } diff --git a/Components/DiskII/IWM.hpp b/Components/DiskII/IWM.hpp index 347fa75bf..fc4427e49 100644 --- a/Components/DiskII/IWM.hpp +++ b/Components/DiskII/IWM.hpp @@ -45,77 +45,77 @@ struct IWMDrive: public Storage::Disk::Drive { class IWM: public Storage::Disk::Drive::EventDelegate, public ClockingHint::Observer { - public: - IWM(int clock_rate); +public: + IWM(int clock_rate); - /// Sets the current external value of the data bus. - void write(int address, uint8_t value); + /// Sets the current external value of the data bus. + void write(int address, uint8_t value); - /*! - Submits an access to address @c address. + /*! + Submits an access to address @c address. - @returns The 8-bit value loaded to the data bus by the IWM. - */ - uint8_t read(int address); + @returns The 8-bit value loaded to the data bus by the IWM. + */ + uint8_t read(int address); - /*! - Sets the current input of the IWM's SEL line. - */ - void set_select(bool enabled); + /*! + Sets the current input of the IWM's SEL line. + */ + void set_select(bool); - /// Advances the controller by @c cycles. - void run_for(const Cycles cycles); + /// Advances the controller. + void run_for(const Cycles); - /// Connects a drive to the IWM. - void set_drive(int slot, IWMDrive *drive); + /// Connects a drive to the IWM. + void set_drive(int slot, IWMDrive *); - /// Registers the currently-connected drives as @c Activity::Sources ; - /// the first will be declared 'Internal', the second 'External'. - void set_activity_observer(Activity::Observer *observer); + /// Registers the currently-connected drives as @c Activity::Sources ; + /// the first will be declared 'Internal', the second 'External'. + void set_activity_observer(Activity::Observer *); - private: - // Storage::Disk::Drive::EventDelegate. - void process_event(const Storage::Disk::Drive::Event &event) final; +private: + // Storage::Disk::Drive::EventDelegate. + void process_event(const Storage::Disk::Drive::Event &) final; - const int clock_rate_; + const int clock_rate_; - uint8_t data_register_ = 0; - uint8_t mode_ = 0; - // These related to functionality not-yet implemented. - // bool read_write_ready_ = true; - // bool write_overran_ = false; + uint8_t data_register_ = 0; + uint8_t mode_ = 0; + // These related to functionality not-yet implemented. + // bool read_write_ready_ = true; + // bool write_overran_ = false; - int state_ = 0; + int state_ = 0; - int active_drive_ = 0; - IWMDrive *drives_[2] = {nullptr, nullptr}; - bool drive_is_rotating_[2] = {false, false}; + int active_drive_ = 0; + IWMDrive *drives_[2] = {nullptr, nullptr}; + bool drive_is_rotating_[2] = {false, false}; - void set_component_prefers_clocking(ClockingHint::Source *component, ClockingHint::Preference clocking) final; + void set_component_prefers_clocking(ClockingHint::Source *, ClockingHint::Preference) final; - Cycles cycles_until_disable_; - uint8_t write_handshake_ = 0x80; + Cycles cycles_until_disable_; + uint8_t write_handshake_ = 0x80; - void access(int address); + void access(int address); - uint8_t shift_register_ = 0; - uint8_t next_output_ = 0; - int output_bits_remaining_ = 0; + uint8_t shift_register_ = 0; + uint8_t next_output_ = 0; + int output_bits_remaining_ = 0; - void propose_shift(uint8_t bit); - Cycles cycles_since_shift_; - Cycles bit_length_ = Cycles(16); + void propose_shift(uint8_t bit); + Cycles cycles_since_shift_; + Cycles bit_length_ = Cycles(16); - void push_drive_state(); + void push_drive_state(); - enum class ShiftMode { - Reading, - Writing, - CheckingWriteProtect - } shift_mode_; + enum class ShiftMode { + Reading, + Writing, + CheckingWriteProtect + } shift_mode_; - uint8_t sense(); - void select_shift_mode(); + uint8_t sense(); + void select_shift_mode(); }; } diff --git a/Components/DiskII/MacintoshDoubleDensityDrive.cpp b/Components/DiskII/MacintoshDoubleDensityDrive.cpp index d90784b76..948578dee 100644 --- a/Components/DiskII/MacintoshDoubleDensityDrive.cpp +++ b/Components/DiskII/MacintoshDoubleDensityDrive.cpp @@ -18,7 +18,7 @@ using namespace Apple::Macintosh; -DoubleDensityDrive::DoubleDensityDrive(int input_clock_rate, bool is_800k) : +DoubleDensityDrive::DoubleDensityDrive(const int input_clock_rate, const bool is_800k) : IWMDrive(input_clock_rate, is_800k ? 2 : 1), // Only 800kb drives are double sided. is_800k_(is_800k) { // Start with a valid rotation speed. @@ -29,7 +29,7 @@ DoubleDensityDrive::DoubleDensityDrive(int input_clock_rate, bool is_800k) : // MARK: - Speed Selection -void DoubleDensityDrive::did_step(Storage::Disk::HeadPosition to_position) { +void DoubleDensityDrive::did_step(const Storage::Disk::HeadPosition to_position) { // The 800kb drive automatically selects rotation speed as a function of // head position; the 400kb drive doesn't do so. if(is_800k_) { @@ -60,7 +60,7 @@ void DoubleDensityDrive::did_step(Storage::Disk::HeadPosition to_position) { } } -void DoubleDensityDrive::set_rotation_speed(float revolutions_per_minute) { +void DoubleDensityDrive::set_rotation_speed(const float revolutions_per_minute) { if(!is_800k_) { // Don't allow drive speeds to drop below 10 RPM, as a temporary sop // to sanity. @@ -70,12 +70,12 @@ void DoubleDensityDrive::set_rotation_speed(float revolutions_per_minute) { // MARK: - Control input/output. -void DoubleDensityDrive::set_enabled(bool enabled) { +void DoubleDensityDrive::set_enabled(const bool enabled) { // Disabling a drive also stops its motor. if(!enabled) set_motor_on(false); } -void DoubleDensityDrive::set_control_lines(int lines) { +void DoubleDensityDrive::set_control_lines(const int lines) { const auto old_state = control_state_; control_state_ = lines; diff --git a/Components/DiskII/MacintoshDoubleDensityDrive.hpp b/Components/DiskII/MacintoshDoubleDensityDrive.hpp index be0f2cc6e..8c7856916 100644 --- a/Components/DiskII/MacintoshDoubleDensityDrive.hpp +++ b/Components/DiskII/MacintoshDoubleDensityDrive.hpp @@ -13,36 +13,36 @@ namespace Apple::Macintosh { class DoubleDensityDrive: public IWMDrive { - public: - DoubleDensityDrive(int input_clock_rate, bool is_800k); +public: + DoubleDensityDrive(int input_clock_rate, bool is_800k); - /*! - @returns @c true if this is an 800kb drive; @c false otherwise. - */ - bool is_800k() const { - return is_800k_; - } + /*! + @returns @c true if this is an 800kb drive; @c false otherwise. + */ + bool is_800k() const { + return is_800k_; + } - /*! - Sets the current rotation speed of this drive only if it is a 400kb drive. - 800kb drives select their own rotation speed based on head position, - and ignore this input. - */ - void set_rotation_speed(float revolutions_per_minute); + /*! + Sets the current rotation speed of this drive only if it is a 400kb drive. + 800kb drives select their own rotation speed based on head position, + and ignore this input. + */ + void set_rotation_speed(float revolutions_per_minute); - private: - void set_enabled(bool) final; - void set_control_lines(int) final; - bool read() final; +private: + void set_enabled(bool) final; + void set_control_lines(int) final; + bool read() final; - // To receive the proper notifications from Storage::Disk::Drive. - void did_step(Storage::Disk::HeadPosition to_position) final; - void did_set_disk(bool) final; + // To receive the proper notifications from Storage::Disk::Drive. + void did_step(Storage::Disk::HeadPosition to_position) final; + void did_set_disk(bool) final; - const bool is_800k_; - bool has_new_disk_ = false; - int control_state_ = 0; - int step_direction_ = 1; + const bool is_800k_; + bool has_new_disk_ = false; + int control_state_ = 0; + int step_direction_ = 1; }; } From 3addb8d72bdac7ae296e0081568709f17b1c05e3 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 30 Nov 2024 17:21:00 -0500 Subject: [PATCH 2/2] Finish updating components. --- Components/I2C/I2C.cpp | 14 +- Components/I2C/I2C.hpp | 4 +- Components/KonamiSCC/KonamiSCC.cpp | 6 +- Components/KonamiSCC/KonamiSCC.hpp | 66 +-- .../OPx/Implementation/EnvelopeGenerator.hpp | 420 +++++++++--------- .../OPx/Implementation/KeyLevelScaler.hpp | 62 +-- .../Implementation/LowFrequencyOscillator.hpp | 64 +-- Components/OPx/Implementation/OPLBase.hpp | 24 +- .../OPx/Implementation/PhaseGenerator.hpp | 160 +++---- Components/OPx/Implementation/Tables.hpp | 10 +- .../OPx/Implementation/WaveformGenerator.hpp | 122 ++--- Components/OPx/OPLL.cpp | 26 +- Components/OPx/OPLL.hpp | 14 +- Components/RP5C01/RP5C01.cpp | 6 +- Components/RP5C01/RP5C01.hpp | 54 +-- Components/SN76489/SN76489.cpp | 18 +- Components/SN76489/SN76489.hpp | 78 ++-- Components/Serial/Line.cpp | 21 +- Components/Serial/Line.hpp | 145 +++--- 19 files changed, 666 insertions(+), 648 deletions(-) diff --git a/Components/I2C/I2C.cpp b/Components/I2C/I2C.cpp index 82b49400e..9066a9b6f 100644 --- a/Components/I2C/I2C.cpp +++ b/Components/I2C/I2C.cpp @@ -18,10 +18,10 @@ Log::Logger logger; } -void Bus::set_data(bool pulled) { +void Bus::set_data(const bool pulled) { set_clock_data(clock_, pulled); } -bool Bus::data() { +bool Bus::data() const { bool result = data_; if(peripheral_bits_) { result |= !(peripheral_response_ & 0x80); @@ -29,14 +29,14 @@ bool Bus::data() { return result; } -void Bus::set_clock(bool pulled) { +void Bus::set_clock(const bool pulled) { set_clock_data(pulled, data_); } -bool Bus::clock() { +bool Bus::clock() const { return clock_; } -void Bus::set_clock_data(bool clock_pulled, bool data_pulled) { +void Bus::set_clock_data(const bool clock_pulled, const bool data_pulled) { // Proceed only if changes are evidenced. if(clock_pulled == clock_ && data_pulled == data_) { return; @@ -95,7 +95,7 @@ void Bus::set_clock_data(bool clock_pulled, bool data_pulled) { } } -void Bus::signal(Event event) { +void Bus::signal(const Event event) { const auto capture_bit = [&]() { input_ = uint16_t((input_ << 1) | (event == Event::Zero ? 0 : 1)); ++input_count_; @@ -221,6 +221,6 @@ void Bus::signal(Event event) { } } -void Bus::add_peripheral(Peripheral *peripheral, int address) { +void Bus::add_peripheral(Peripheral *const peripheral, const int address) { peripherals_[address] = peripheral; } diff --git a/Components/I2C/I2C.hpp b/Components/I2C/I2C.hpp index e8dc0c783..e9a1bd80e 100644 --- a/Components/I2C/I2C.hpp +++ b/Components/I2C/I2C.hpp @@ -41,10 +41,10 @@ struct Peripheral { class Bus { public: void set_data(bool pulled); - bool data(); + bool data() const; void set_clock(bool pulled); - bool clock(); + bool clock() const; void set_clock_data(bool clock_pulled, bool data_pulled); diff --git a/Components/KonamiSCC/KonamiSCC.cpp b/Components/KonamiSCC/KonamiSCC.cpp index c4c1abc8d..360a635b5 100644 --- a/Components/KonamiSCC/KonamiSCC.cpp +++ b/Components/KonamiSCC/KonamiSCC.cpp @@ -20,7 +20,7 @@ bool SCC::is_zero_level() const { } template -void SCC::apply_samples(std::size_t number_of_samples, Outputs::Speaker::MonoSample *target) { +void SCC::apply_samples(const std::size_t number_of_samples, Outputs::Speaker::MonoSample *const target) { if(is_zero_level()) { Outputs::Speaker::fill(target, target + number_of_samples, Outputs::Speaker::MonoSample()); return; @@ -55,7 +55,7 @@ template void SCC::apply_samples(std::size_t, Out template void SCC::apply_samples(std::size_t, Outputs::Speaker::MonoSample *); template void SCC::apply_samples(std::size_t, Outputs::Speaker::MonoSample *); -void SCC::write(uint16_t address, uint8_t value) { +void SCC::write(uint16_t address, const uint8_t value) { address &= 0xff; if(address < 0x80) ram_[address] = value; @@ -108,7 +108,7 @@ void SCC::set_sample_volume_range(std::int16_t range) { evaluate_output_volume(); } -uint8_t SCC::read(uint16_t address) { +uint8_t SCC::read(uint16_t address) const { address &= 0xff; if(address < 0x80) { return ram_[address]; diff --git a/Components/KonamiSCC/KonamiSCC.hpp b/Components/KonamiSCC/KonamiSCC.hpp index 5a379c3d0..9c6607189 100644 --- a/Components/KonamiSCC/KonamiSCC.hpp +++ b/Components/KonamiSCC/KonamiSCC.hpp @@ -21,51 +21,51 @@ namespace Konami { four and five, the SCC+ supports different waves for the two channels. */ class SCC: public ::Outputs::Speaker::BufferSource { - public: - /// Creates a new SCC. - SCC(Concurrency::AsyncTaskQueue &task_queue); +public: + /// Creates a new SCC. + SCC(Concurrency::AsyncTaskQueue &); - /// As per ::SampleSource; provides a broadphase test for silence. - bool is_zero_level() const; + /// As per ::SampleSource; provides a broadphase test for silence. + bool is_zero_level() const; - /// As per ::SampleSource; provides audio output. - template - void apply_samples(std::size_t number_of_samples, Outputs::Speaker::MonoSample *target); - void set_sample_volume_range(std::int16_t range); + /// As per ::SampleSource; provides audio output. + template + void apply_samples(std::size_t number_of_samples, Outputs::Speaker::MonoSample *); + void set_sample_volume_range(std::int16_t); - /// Writes to the SCC. - void write(uint16_t address, uint8_t value); + /// Writes to the SCC. + void write(uint16_t address, uint8_t value); - /// Reads from the SCC. - uint8_t read(uint16_t address); + /// Reads from the SCC. + uint8_t read(uint16_t address) const; - private: - Concurrency::AsyncTaskQueue &task_queue_; +private: + Concurrency::AsyncTaskQueue &task_queue_; - // State from here on down is accessed ony from the audio thread. - int master_divider_ = 0; - std::int16_t master_volume_ = 0; - Outputs::Speaker::MonoSample transient_output_level_ = 0; + // State from here on down is accessed ony from the audio thread. + int master_divider_ = 0; + std::int16_t master_volume_ = 0; + Outputs::Speaker::MonoSample transient_output_level_ = 0; - struct Channel { - int period = 0; - int amplitude = 0; + struct Channel { + int period = 0; + int amplitude = 0; - int tone_counter = 0; - int offset = 0; - } channels_[5]; + int tone_counter = 0; + int offset = 0; + } channels_[5]; - struct Wavetable { - std::uint8_t samples[32]; - } waves_[4]; + struct Wavetable { + std::uint8_t samples[32]; + } waves_[4]; - std::uint8_t channel_enable_ = 0; + std::uint8_t channel_enable_ = 0; - void evaluate_output_volume(); + void evaluate_output_volume(); - // This keeps a copy of wave memory that is accessed from the - // main emulation thread. - std::uint8_t ram_[128]; + // This keeps a copy of wave memory that is accessed from the + // main emulation thread. + std::uint8_t ram_[128]; }; } diff --git a/Components/OPx/Implementation/EnvelopeGenerator.hpp b/Components/OPx/Implementation/EnvelopeGenerator.hpp index e9f592a5d..64554bd83 100644 --- a/Components/OPx/Implementation/EnvelopeGenerator.hpp +++ b/Components/OPx/Implementation/EnvelopeGenerator.hpp @@ -31,229 +31,229 @@ namespace Yamaha::OPL { TODO: use envelope_precision. */ template class EnvelopeGenerator { - public: - /*! - Advances the envelope generator a single step, given the current state of the low-frequency oscillator, @c oscillator. - */ - void update(const LowFrequencyOscillator &oscillator) { - // Apply tremolo, which is fairly easy. - tremolo_ = tremolo_enable_ * oscillator.tremolo << 4; +public: + /*! + Advances the envelope generator a single step, given the current state of the low-frequency oscillator, @c oscillator. + */ + void update(const LowFrequencyOscillator &oscillator) { + // Apply tremolo, which is fairly easy. + tremolo_ = tremolo_enable_ * oscillator.tremolo << 4; - // Something something something... - const int key_scaling_rate = key_scale_rate_ >> key_scale_rate_shift_; - switch(phase_) { - case Phase::Damp: - update_decay(oscillator, 12 << 2); - if(attenuation_ == 511) { - (*will_attack_)(); - phase_ = Phase::Attack; - } - break; - - case Phase::Attack: - update_attack(oscillator, attack_rate_ + key_scaling_rate); - - // Two possible terminating conditions: (i) the attack rate is 15; (ii) full volume has been reached. - if(attenuation_ <= 0) { - attenuation_ = 0; - phase_ = Phase::Decay; - } - break; - - case Phase::Decay: - update_decay(oscillator, decay_rate_ + key_scaling_rate); - if(attenuation_ >= sustain_level_) { - attenuation_ = sustain_level_; - phase_ = use_sustain_level_ ? Phase::Sustain : Phase::Release; - } - break; - - case Phase::Sustain: - // Nothing to do. - break; - - case Phase::Release: - update_decay(oscillator, release_rate_ + key_scaling_rate); - break; - } - } - - /*! - @returns The current attenuation from this envelope generator. This is independent of the envelope precision. - */ - int attenuation() const { - // TODO: if this envelope is fully released, should tremolo still be able to vocalise it? - return (attenuation_ << 3) + tremolo_; - } - - /*! - Enables or disables damping on this envelope generator. If damping is enabled then this envelope generator will - use the damping phase when necessary (i.e. when transitioning to key on if attenuation is not already at maximum) - and in any case will call @c will_attack before transitioning from any other state to attack. - - @param will_attack Supply a will_attack callback to enable damping mode; supply nullopt to disable damping mode. - */ - void set_should_damp(const std::optional> &will_attack) { - will_attack_ = will_attack; - } - - /*! - Sets the current state of the key-on input. - */ - void set_key_on(bool key_on) { - // Do nothing if this is not a leading or trailing edge. - if(key_on == key_on_) return; - key_on_ = key_on; - - // Always transition to release upon a key off. - if(!key_on_) { - phase_ = Phase::Release; - return; - } - - // On key on: if this is an envelope generator with damping, and damping is required, - // schedule that. If damping is not required, announce a pending attack now and - // transition to attack. - if(will_attack_) { - if(attenuation_ != 511) { - phase_ = Phase::Damp; - return; + // Something something something... + const int key_scaling_rate = key_scale_rate_ >> key_scale_rate_shift_; + switch(phase_) { + case Phase::Damp: + update_decay(oscillator, 12 << 2); + if(attenuation_ == 511) { + (*will_attack_)(); + phase_ = Phase::Attack; } + break; - (*will_attack_)(); - } - phase_ = Phase::Attack; + case Phase::Attack: + update_attack(oscillator, attack_rate_ + key_scaling_rate); + + // Two possible terminating conditions: (i) the attack rate is 15; (ii) full volume has been reached. + if(attenuation_ <= 0) { + attenuation_ = 0; + phase_ = Phase::Decay; + } + break; + + case Phase::Decay: + update_decay(oscillator, decay_rate_ + key_scaling_rate); + if(attenuation_ >= sustain_level_) { + attenuation_ = sustain_level_; + phase_ = use_sustain_level_ ? Phase::Sustain : Phase::Release; + } + break; + + case Phase::Sustain: + // Nothing to do. + break; + + case Phase::Release: + update_decay(oscillator, release_rate_ + key_scaling_rate); + break; + } + } + + /*! + @returns The current attenuation from this envelope generator. This is independent of the envelope precision. + */ + int attenuation() const { + // TODO: if this envelope is fully released, should tremolo still be able to vocalise it? + return (attenuation_ << 3) + tremolo_; + } + + /*! + Enables or disables damping on this envelope generator. If damping is enabled then this envelope generator will + use the damping phase when necessary (i.e. when transitioning to key on if attenuation is not already at maximum) + and in any case will call @c will_attack before transitioning from any other state to attack. + + @param will_attack Supply a will_attack callback to enable damping mode; supply nullopt to disable damping mode. + */ + void set_should_damp(const std::optional> &will_attack) { + will_attack_ = will_attack; + } + + /*! + Sets the current state of the key-on input. + */ + void set_key_on(const bool key_on) { + // Do nothing if this is not a leading or trailing edge. + if(key_on == key_on_) return; + key_on_ = key_on; + + // Always transition to release upon a key off. + if(!key_on_) { + phase_ = Phase::Release; + return; } - /*! - Sets the attack rate, which should be in the range 0–15. - */ - void set_attack_rate(int rate) { - attack_rate_ = rate << 2; - } - - /*! - Sets the decay rate, which should be in the range 0–15. - */ - void set_decay_rate(int rate) { - decay_rate_ = rate << 2; - } - - /*! - Sets the release rate, which should be in the range 0–15. - */ - void set_release_rate(int rate) { - release_rate_ = rate << 2; - } - - /*! - Sets the sustain level, which should be in the range 0–15. - */ - void set_sustain_level(int level) { - sustain_level_ = level << 3; - // TODO: verify the shift level here. Especially re: precision. - } - - /*! - Enables or disables use of the sustain level. If this is disabled, the envelope proceeds - directly from decay to release. - */ - void set_use_sustain_level(bool use) { - use_sustain_level_ = use; - } - - /*! - Enables or disables key-rate scaling. - */ - void set_key_scaling_rate_enabled(bool enabled) { - key_scale_rate_shift_ = int(enabled) * 2; - } - - /*! - Enables or disables application of the low-frequency oscillator's tremolo. - */ - void set_tremolo_enabled(bool enabled) { - tremolo_enable_ = int(enabled); - } - - /*! - Sets the current period associated with the channel that owns this envelope generator; - this is used to select a key scaling rate if key-rate scaling is enabled. - */ - void set_period(int period, int octave) { - key_scale_rate_ = (octave << 1) | (period >> (period_precision - 1)); - } - - private: - enum class Phase { - Attack, Decay, Sustain, Release, Damp - } phase_ = Phase::Release; - int attenuation_ = 511, tremolo_ = 0; - - bool key_on_ = false; - std::optional> will_attack_; - - int key_scale_rate_ = 0; - int key_scale_rate_shift_ = 0; - - int tremolo_enable_ = 0; - - int attack_rate_ = 0; - int decay_rate_ = 0; - int release_rate_ = 0; - int sustain_level_ = 0; - bool use_sustain_level_ = false; - - static constexpr int dithering_patterns[4][8] = { - {0, 1, 0, 1, 0, 1, 0, 1}, - {0, 1, 0, 1, 1, 1, 0, 1}, - {0, 1, 1, 1, 0, 1, 1, 1}, - {0, 1, 1, 1, 1, 1, 1, 1}, - }; - - void update_attack(const LowFrequencyOscillator &oscillator, int rate) { - // Special case: no attack. - if(rate < 4) { + // On key on: if this is an envelope generator with damping, and damping is required, + // schedule that. If damping is not required, announce a pending attack now and + // transition to attack. + if(will_attack_) { + if(attenuation_ != 511) { + phase_ = Phase::Damp; return; } - // Special case: instant attack. - if(rate >= 60) { - attenuation_ = 0; - return; - } + (*will_attack_)(); + } + phase_ = Phase::Attack; + } - // Work out the number of cycles between each adjustment tick, and stop now - // if not at the next adjustment boundary. - const int shift_size = 13 - (std::min(rate, 52) >> 2); - if(oscillator.counter & ((1 << shift_size) - 1)) { - return; - } + /*! + Sets the attack rate, which should be in the range 0–15. + */ + void set_attack_rate(const int rate) { + attack_rate_ = rate << 2; + } - // Apply dithered adjustment. - const int rate_shift = (rate > 55); - const int step = dithering_patterns[rate & 3][(oscillator.counter >> shift_size) & 7]; - attenuation_ -= ((attenuation_ >> (3 - rate_shift)) + 1) * step; + /*! + Sets the decay rate, which should be in the range 0–15. + */ + void set_decay_rate(const int rate) { + decay_rate_ = rate << 2; + } + + /*! + Sets the release rate, which should be in the range 0–15. + */ + void set_release_rate(const int rate) { + release_rate_ = rate << 2; + } + + /*! + Sets the sustain level, which should be in the range 0–15. + */ + void set_sustain_level(const int level) { + sustain_level_ = level << 3; + // TODO: verify the shift level here. Especially re: precision. + } + + /*! + Enables or disables use of the sustain level. If this is disabled, the envelope proceeds + directly from decay to release. + */ + void set_use_sustain_level(const bool use) { + use_sustain_level_ = use; + } + + /*! + Enables or disables key-rate scaling. + */ + void set_key_scaling_rate_enabled(const bool enabled) { + key_scale_rate_shift_ = int(enabled) * 2; + } + + /*! + Enables or disables application of the low-frequency oscillator's tremolo. + */ + void set_tremolo_enabled(const bool enabled) { + tremolo_enable_ = int(enabled); + } + + /*! + Sets the current period associated with the channel that owns this envelope generator; + this is used to select a key scaling rate if key-rate scaling is enabled. + */ + void set_period(const int period, const int octave) { + key_scale_rate_ = (octave << 1) | (period >> (period_precision - 1)); + } + +private: + enum class Phase { + Attack, Decay, Sustain, Release, Damp + } phase_ = Phase::Release; + int attenuation_ = 511, tremolo_ = 0; + + bool key_on_ = false; + std::optional> will_attack_; + + int key_scale_rate_ = 0; + int key_scale_rate_shift_ = 0; + + int tremolo_enable_ = 0; + + int attack_rate_ = 0; + int decay_rate_ = 0; + int release_rate_ = 0; + int sustain_level_ = 0; + bool use_sustain_level_ = false; + + static constexpr int dithering_patterns[4][8] = { + {0, 1, 0, 1, 0, 1, 0, 1}, + {0, 1, 0, 1, 1, 1, 0, 1}, + {0, 1, 1, 1, 0, 1, 1, 1}, + {0, 1, 1, 1, 1, 1, 1, 1}, + }; + + void update_attack(const LowFrequencyOscillator &oscillator, const int rate) { + // Special case: no attack. + if(rate < 4) { + return; } - void update_decay(const LowFrequencyOscillator &oscillator, int rate) { - // Special case: no decay. - if(rate < 4) { - return; - } - - // Work out the number of cycles between each adjustment tick, and stop now - // if not at the next adjustment boundary. - const int shift_size = 13 - (std::min(rate, 52) >> 2); - if(oscillator.counter & ((1 << shift_size) - 1)) { - return; - } - - // Apply dithered adjustment and clamp. - const int rate_shift = 1 + (rate > 59) + (rate > 55); - attenuation_ += dithering_patterns[rate & 3][(oscillator.counter >> shift_size) & 7] * (4 << rate_shift); - attenuation_ = std::min(attenuation_, 511); + // Special case: instant attack. + if(rate >= 60) { + attenuation_ = 0; + return; } + + // Work out the number of cycles between each adjustment tick, and stop now + // if not at the next adjustment boundary. + const int shift_size = 13 - (std::min(rate, 52) >> 2); + if(oscillator.counter & ((1 << shift_size) - 1)) { + return; + } + + // Apply dithered adjustment. + const int rate_shift = (rate > 55); + const int step = dithering_patterns[rate & 3][(oscillator.counter >> shift_size) & 7]; + attenuation_ -= ((attenuation_ >> (3 - rate_shift)) + 1) * step; + } + + void update_decay(const LowFrequencyOscillator &oscillator, const int rate) { + // Special case: no decay. + if(rate < 4) { + return; + } + + // Work out the number of cycles between each adjustment tick, and stop now + // if not at the next adjustment boundary. + const int shift_size = 13 - (std::min(rate, 52) >> 2); + if(oscillator.counter & ((1 << shift_size) - 1)) { + return; + } + + // Apply dithered adjustment and clamp. + const int rate_shift = 1 + (rate > 59) + (rate > 55); + attenuation_ += dithering_patterns[rate & 3][(oscillator.counter >> shift_size) & 7] * (4 << rate_shift); + attenuation_ = std::min(attenuation_, 511); + } }; } diff --git a/Components/OPx/Implementation/KeyLevelScaler.hpp b/Components/OPx/Implementation/KeyLevelScaler.hpp index fe4e59243..67909f30f 100644 --- a/Components/OPx/Implementation/KeyLevelScaler.hpp +++ b/Components/OPx/Implementation/KeyLevelScaler.hpp @@ -11,42 +11,42 @@ namespace Yamaha::OPL { template class KeyLevelScaler { - public: +public: - /*! - Sets the current period associated with the channel that owns this envelope generator; - this is used to select a key scaling rate if key-rate scaling is enabled. - */ - void set_period(int period, int octave) { - constexpr int key_level_scales[16] = {0, 48, 64, 74, 80, 86, 90, 94, 96, 100, 102, 104, 106, 108, 110, 112}; - constexpr int masks[2] = {~0, 0}; + /*! + Sets the current period associated with the channel that owns this envelope generator; + this is used to select a key scaling rate if key-rate scaling is enabled. + */ + void set_period(const int period, const int octave) { + constexpr int key_level_scales[16] = {0, 48, 64, 74, 80, 86, 90, 94, 96, 100, 102, 104, 106, 108, 110, 112}; + constexpr int masks[2] = {~0, 0}; - // A two's complement assumption is embedded below; the use of masks relies - // on the sign bit to clamp to zero. - level_ = key_level_scales[period >> (frequency_precision - 4)]; - level_ -= 16 * (octave ^ 7); - level_ &= masks[(level_ >> ((sizeof(int) * 8) - 1)) & 1]; - } + // A two's complement assumption is embedded below; the use of masks relies + // on the sign bit to clamp to zero. + level_ = key_level_scales[period >> (frequency_precision - 4)]; + level_ -= 16 * (octave ^ 7); + level_ &= masks[(level_ >> ((sizeof(int) * 8) - 1)) & 1]; + } - /*! - Enables or disables key-rate scaling. - */ - void set_key_scaling_level(int level) { - // '7' is just a number large enough to render all possible scaling coefficients as 0. - constexpr int key_level_scale_shifts[4] = {7, 1, 2, 0}; - shift_ = key_level_scale_shifts[level]; - } + /*! + Enables or disables key-rate scaling. + */ + void set_key_scaling_level(const int level) { + // '7' is just a number large enough to render all possible scaling coefficients as 0. + constexpr int key_level_scale_shifts[4] = {7, 1, 2, 0}; + shift_ = key_level_scale_shifts[level]; + } - /*! - @returns The current attenuation level due to key-level scaling. - */ - int attenuation() const { - return level_ >> shift_; - } + /*! + @returns The current attenuation level due to key-level scaling. + */ + int attenuation() const { + return level_ >> shift_; + } - private: - int level_ = 0; - int shift_ = 0; +private: + int level_ = 0; + int shift_ = 0; }; } diff --git a/Components/OPx/Implementation/LowFrequencyOscillator.hpp b/Components/OPx/Implementation/LowFrequencyOscillator.hpp index bc8877ef8..66e017b9b 100644 --- a/Components/OPx/Implementation/LowFrequencyOscillator.hpp +++ b/Components/OPx/Implementation/LowFrequencyOscillator.hpp @@ -18,46 +18,46 @@ namespace Yamaha::OPL { as part of their ADSR envelope. */ class LowFrequencyOscillator { - public: - /// Current attenuation due to tremolo / amplitude modulation, between 0 and 26. - int tremolo = 0; +public: + /// Current attenuation due to tremolo / amplitude modulation, between 0 and 26. + int tremolo = 0; - /// A number between 0 and 7 indicating the current vibrato offset; this should be combined by operators - /// with their frequency number to get the actual vibrato. - int vibrato = 0; + /// A number between 0 and 7 indicating the current vibrato offset; this should be combined by operators + /// with their frequency number to get the actual vibrato. + int vibrato = 0; - /// A counter of the number of operator update cycles (i.e. input clock / 72) since an arbitrary time. - int counter = 0; + /// A counter of the number of operator update cycles (i.e. input clock / 72) since an arbitrary time. + int counter = 0; - /// Describes the current output of the LFSR; will be either 0 or 1. - int lfsr = 0; + /// Describes the current output of the LFSR; will be either 0 or 1. + int lfsr = 0; - /// Updates the oscillator outputs. Should be called at the (input clock/72) rate. - void update() { - ++counter; + /// Updates the oscillator outputs. Should be called at the (input clock/72) rate. + void update() { + ++counter; - // This produces output of: - // - // four instances of 0, four instances of 1... _three_ instances of 26, - // four instances of 25, four instances of 24... _three_ instances of 0. - // - // ... advancing once every 64th update. - const int tremolo_index = (counter >> 6) % 210; - const int tremolo_levels[2] = {tremolo_index >> 2, 52 - ((tremolo_index+1) >> 2)}; - tremolo = tremolo_levels[tremolo_index / 107]; + // This produces output of: + // + // four instances of 0, four instances of 1... _three_ instances of 26, + // four instances of 25, four instances of 24... _three_ instances of 0. + // + // ... advancing once every 64th update. + const int tremolo_index = (counter >> 6) % 210; + const int tremolo_levels[2] = {tremolo_index >> 2, 52 - ((tremolo_index+1) >> 2)}; + tremolo = tremolo_levels[tremolo_index / 107]; - // Vibrato is relatively simple: it's just three bits from the counter. - vibrato = (counter >> 10) & 7; - } + // Vibrato is relatively simple: it's just three bits from the counter. + vibrato = (counter >> 10) & 7; + } - /// Updartes the LFSR output. Should be called at the input clock rate. - void update_lfsr() { - lfsr = noise_source_.next(); - } + /// Updartes the LFSR output. Should be called at the input clock rate. + void update_lfsr() { + lfsr = noise_source_.next(); + } - private: - // This is the correct LSFR per forums.submarine.org.uk. - Numeric::LFSR noise_source_; +private: + // This is the correct LSFR per forums.submarine.org.uk. + Numeric::LFSR noise_source_; }; } diff --git a/Components/OPx/Implementation/OPLBase.hpp b/Components/OPx/Implementation/OPLBase.hpp index 1890646a9..990c0783e 100644 --- a/Components/OPx/Implementation/OPLBase.hpp +++ b/Components/OPx/Implementation/OPLBase.hpp @@ -14,22 +14,22 @@ namespace Yamaha::OPL { template class OPLBase: public ::Outputs::Speaker::BufferSource { - public: - void write(uint16_t address, uint8_t value) { - if(address & 1) { - static_cast(this)->write_register(selected_register_, value); - } else { - selected_register_ = value; - } +public: + void write(const uint16_t address, const uint8_t value) { + if(address & 1) { + static_cast(this)->write_register(selected_register_, value); + } else { + selected_register_ = value; } + } - protected: - OPLBase(Concurrency::AsyncTaskQueue &task_queue) : task_queue_(task_queue) {} +protected: + OPLBase(Concurrency::AsyncTaskQueue &task_queue) : task_queue_(task_queue) {} - Concurrency::AsyncTaskQueue &task_queue_; + Concurrency::AsyncTaskQueue &task_queue_; - private: - uint8_t selected_register_ = 0; +private: + uint8_t selected_register_ = 0; }; } diff --git a/Components/OPx/Implementation/PhaseGenerator.hpp b/Components/OPx/Implementation/PhaseGenerator.hpp index f1a17f076..520d4a55a 100644 --- a/Components/OPx/Implementation/PhaseGenerator.hpp +++ b/Components/OPx/Implementation/PhaseGenerator.hpp @@ -19,102 +19,102 @@ namespace Yamaha::OPL { multiple, and whether to apply vibrato, this will then appropriately update and return phase. */ template class PhaseGenerator { - public: - /*! - Advances the phase generator a single step, given the current state of the low-frequency oscillator, @c oscillator. - */ - void update(const LowFrequencyOscillator &oscillator) { - constexpr int vibrato_shifts[4] = {3, 1, 0, 1}; - constexpr int vibrato_signs[2] = {1, -1}; +public: + /*! + Advances the phase generator a single step, given the current state of the low-frequency oscillator, @c oscillator. + */ + void update(const LowFrequencyOscillator &oscillator) { + constexpr int vibrato_shifts[4] = {3, 1, 0, 1}; + constexpr int vibrato_signs[2] = {1, -1}; - // Get just the top three bits of the period_. - const int top_freq = period_ >> (precision - 3); + // Get just the top three bits of the period_. + const int top_freq = period_ >> (precision - 3); - // Cacluaute applicable vibrato as a function of (i) the top three bits of the - // oscillator period; (ii) the current low-frequency oscillator vibrato output; and - // (iii) whether vibrato is enabled. - const int vibrato = (top_freq >> vibrato_shifts[oscillator.vibrato & 3]) * vibrato_signs[oscillator.vibrato >> 2] * enable_vibrato_; + // Cacluaute applicable vibrato as a function of (i) the top three bits of the + // oscillator period; (ii) the current low-frequency oscillator vibrato output; and + // (iii) whether vibrato is enabled. + const int vibrato = (top_freq >> vibrato_shifts[oscillator.vibrato & 3]) * vibrato_signs[oscillator.vibrato >> 2] * enable_vibrato_; - // Apply phase update with vibrato from the low-frequency oscillator. - phase_ += (multiple_ * ((period_ << 1) + vibrato) << octave_) >> 1; - } + // Apply phase update with vibrato from the low-frequency oscillator. + phase_ += (multiple_ * ((period_ << 1) + vibrato) << octave_) >> 1; + } - /*! - @returns Current phase; real hardware provides only the low ten bits of this result. - */ - int phase() const { - // My table if multipliers is multiplied by two, so shift by one more - // than the stated precision. - return phase_ >> precision_shift; - } + /*! + @returns Current phase; real hardware provides only the low ten bits of this result. + */ + int phase() const { + // My table if multipliers is multiplied by two, so shift by one more + // than the stated precision. + return phase_ >> precision_shift; + } - /*! - @returns Current phase, scaled up by (1 << precision). - */ - int scaled_phase() const { - return phase_ >> 1; - } + /*! + @returns Current phase, scaled up by (1 << precision). + */ + int scaled_phase() const { + return phase_ >> 1; + } - /*! - Applies feedback based on two historic samples of a total output level, - plus the degree of feedback to apply - */ - void apply_feedback(LogSign first, LogSign second, int level) { - constexpr int masks[] = {0, ~0, ~0, ~0, ~0, ~0, ~0, ~0}; - phase_ += ((second.level(precision) + first.level(precision)) >> (8 - level)) & masks[level]; - } + /*! + Applies feedback based on two historic samples of a total output level, + plus the degree of feedback to apply + */ + void apply_feedback(const LogSign first, const LogSign second, const int level) { + constexpr int masks[] = {0, ~0, ~0, ~0, ~0, ~0, ~0, ~0}; + phase_ += ((second.level(precision) + first.level(precision)) >> (8 - level)) & masks[level]; + } - /*! - Sets the multiple for this phase generator, in the same terms as an OPL programmer, - i.e. a 4-bit number that is used as a lookup into the internal multiples table. - */ - void set_multiple(int multiple) { - // This encodes the MUL -> multiple table given on page 12, - // multiplied by two. - constexpr int multipliers[] = { - 1, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 20, 24, 24, 30, 30 - }; - assert(multiple < 16); - multiple_ = multipliers[multiple]; - } + /*! + Sets the multiple for this phase generator, in the same terms as an OPL programmer, + i.e. a 4-bit number that is used as a lookup into the internal multiples table. + */ + void set_multiple(const int multiple) { + // This encodes the MUL -> multiple table given on page 12, + // multiplied by two. + constexpr int multipliers[] = { + 1, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 20, 24, 24, 30, 30 + }; + assert(multiple < 16); + multiple_ = multipliers[multiple]; + } - /*! - Sets the period of this generator, along with its current octave. + /*! + Sets the period of this generator, along with its current octave. - Yamaha tends to refer to the period as the 'f-number', and used both 'octave' and 'block' for octave. - */ - void set_period(int period, int octave) { - period_ = period; - octave_ = octave; + Yamaha tends to refer to the period as the 'f-number', and used both 'octave' and 'block' for octave. + */ + void set_period(const int period, const int octave) { + period_ = period; + octave_ = octave; - assert(octave_ < 8); - assert(period_ < (1 << precision)); - } + assert(octave_ < 8); + assert(period_ < (1 << precision)); + } - /*! - Enables or disables vibrato. - */ - void set_vibrato_enabled(bool enabled) { - enable_vibrato_ = int(enabled); - } + /*! + Enables or disables vibrato. + */ + void set_vibrato_enabled(const bool enabled) { + enable_vibrato_ = int(enabled); + } - /*! - Resets the current phase. - */ - void reset() { - phase_ = 0; - } + /*! + Resets the current phase. + */ + void reset() { + phase_ = 0; + } - private: - static constexpr int precision_shift = 1 + precision; +private: + static constexpr int precision_shift = 1 + precision; - int phase_ = 0; + int phase_ = 0; - int multiple_ = 0; - int period_ = 0; - int octave_ = 0; - int enable_vibrato_ = 0; + int multiple_ = 0; + int period_ = 0; + int octave_ = 0; + int enable_vibrato_ = 0; }; } diff --git a/Components/OPx/Implementation/Tables.hpp b/Components/OPx/Implementation/Tables.hpp index bcdbe6593..91433612e 100644 --- a/Components/OPx/Implementation/Tables.hpp +++ b/Components/OPx/Implementation/Tables.hpp @@ -32,12 +32,12 @@ struct LogSign { sign = 1; } - LogSign &operator +=(int attenuation) { + LogSign &operator +=(const int attenuation) { log += attenuation; return *this; } - LogSign &operator +=(LogSign log_sign) { + LogSign &operator +=(const LogSign log_sign) { log += log_sign.log; sign *= log_sign.sign; return *this; @@ -49,7 +49,7 @@ struct LogSign { /*! @returns Negative log sin of x, assuming a 1024-unit circle. */ -constexpr LogSign negative_log_sin(int x) { +constexpr LogSign negative_log_sin(const int x) { /// Defines the first quadrant of 1024-unit negative log to the base two of sine (that conveniently misses sin(0)). /// /// Expected branchless usage for a full 1024 unit output: @@ -107,7 +107,7 @@ constexpr LogSign negative_log_sin(int x) { Computes the linear value represented by the log-sign @c ls, shifted left by @c fractional prior to loss of precision. */ -constexpr int power_two(LogSign ls, int fractional = 0) { +constexpr int power_two(const LogSign ls, const int fractional = 0) { /// A derivative of the exponent table in a real OPL2; mapped_exp[x] = (source[c ^ 0xff] << 1) | 0x800. /// /// The ahead-of-time transformation represents fixed work the OPL2 does when reading its table @@ -215,7 +215,7 @@ constexpr uint8_t percussion_patch_set[] = { }; -inline int LogSign::level(int fractional) const { +inline int LogSign::level(const int fractional) const { return power_two(*this, fractional); } diff --git a/Components/OPx/Implementation/WaveformGenerator.hpp b/Components/OPx/Implementation/WaveformGenerator.hpp index 8576e3c2c..2bf5c9810 100644 --- a/Components/OPx/Implementation/WaveformGenerator.hpp +++ b/Components/OPx/Implementation/WaveformGenerator.hpp @@ -18,70 +18,74 @@ enum class Waveform { }; template class WaveformGenerator { - public: - /*! - @returns The output of waveform @c form at [integral] phase @c phase. - */ - static constexpr LogSign wave(Waveform form, int phase) { - constexpr int waveforms[4][4] = { - {1023, 1023, 1023, 1023}, // Sine: don't mask in any quadrant. - {511, 511, 0, 0}, // Half sine: keep the first half intact, lock to 0 in the second half. - {511, 511, 511, 511}, // AbsSine: endlessly repeat the first half of the sine wave. - {255, 0, 255, 0}, // PulseSine: act as if the first quadrant is in the first and third; lock the other two to 0. - }; - return negative_log_sin(phase & waveforms[int(form)][(phase >> 8) & 3]); - } +public: + /*! + @returns The output of waveform @c form at [integral] phase @c phase. + */ + static constexpr LogSign wave(Waveform form, int phase) { + constexpr int waveforms[4][4] = { + {1023, 1023, 1023, 1023}, // Sine: don't mask in any quadrant. + {511, 511, 0, 0}, // Half sine: keep the first half intact, lock to 0 in the second half. + {511, 511, 511, 511}, // AbsSine: endlessly repeat the first half of the sine wave. + {255, 0, 255, 0}, // PulseSine: act as if the first quadrant is in the first and third; lock the other two to 0. + }; + return negative_log_sin(phase & waveforms[int(form)][(phase >> 8) & 3]); + } - /*! - @returns The output of waveform @c form at [scaled] phase @c scaled_phase given the modulation input @c modulation. - */ - static constexpr LogSign wave(Waveform form, int scaled_phase, LogSign modulation) { - const int scaled_phase_offset = modulation.level(phase_precision); - const int phase = (scaled_phase + scaled_phase_offset) >> phase_precision; - return wave(form, phase); - } + /*! + @returns The output of waveform @c form at [scaled] phase @c scaled_phase given the modulation input @c modulation. + */ + static constexpr LogSign wave(const Waveform form, const int scaled_phase, const LogSign modulation) { + const int scaled_phase_offset = modulation.level(phase_precision); + const int phase = (scaled_phase + scaled_phase_offset) >> phase_precision; + return wave(form, phase); + } - /*! - @returns Snare output, calculated from the current LFSR state as captured in @c oscillator and an operator's phase. - */ - static constexpr LogSign snare(const LowFrequencyOscillator &oscillator, int phase) { - // If noise is 0, output is positive. - // If noise is 1, output is negative. - // If (noise ^ sign) is 0, output is 0. Otherwise it is max. - const int sign = phase & 0x200; - const int level = ((phase >> 9) & 1) ^ oscillator.lfsr; - return negative_log_sin(sign + (level << 8)); - } + /*! + @returns Snare output, calculated from the current LFSR state as captured in @c oscillator and an operator's phase. + */ + static constexpr LogSign snare(const LowFrequencyOscillator &oscillator, const int phase) { + // If noise is 0, output is positive. + // If noise is 1, output is negative. + // If (noise ^ sign) is 0, output is 0. Otherwise it is max. + const int sign = phase & 0x200; + const int level = ((phase >> 9) & 1) ^ oscillator.lfsr; + return negative_log_sin(sign + (level << 8)); + } - /*! - @returns Cymbal output, calculated from an operator's phase and a modulator's phase. - */ - static constexpr LogSign cymbal(int carrier_phase, int modulator_phase) { - return negative_log_sin(256 + (phase_combination(carrier_phase, modulator_phase) << 9)); - } + /*! + @returns Cymbal output, calculated from an operator's phase and a modulator's phase. + */ + static constexpr LogSign cymbal(const int carrier_phase, const int modulator_phase) { + return negative_log_sin(256 + (phase_combination(carrier_phase, modulator_phase) << 9)); + } - /*! - @returns High-hat output, calculated from the current LFSR state as captured in @c oscillator, an operator's phase and a modulator's phase. - */ - static constexpr LogSign high_hat(const LowFrequencyOscillator &oscillator, int carrier_phase, int modulator_phase) { - constexpr int angles[] = {0x234, 0xd0, 0x2d0, 0x34}; - return negative_log_sin(angles[ - phase_combination(carrier_phase, modulator_phase) | - (oscillator.lfsr << 1) - ]); - } + /*! + @returns High-hat output, calculated from the current LFSR state as captured in @c oscillator, an operator's phase and a modulator's phase. + */ + static constexpr LogSign high_hat( + const LowFrequencyOscillator &oscillator, + const int carrier_phase, + const int modulator_phase + ) { + constexpr int angles[] = {0x234, 0xd0, 0x2d0, 0x34}; + return negative_log_sin(angles[ + phase_combination(carrier_phase, modulator_phase) | + (oscillator.lfsr << 1) + ]); + } - private: - /*! - @returns The phase bit used for cymbal and high-hat generation, which is a function of two operators' phases. - */ - static constexpr int phase_combination(int carrier_phase, int modulator_phase) { - return ( - ((carrier_phase >> 5) ^ (carrier_phase >> 3)) & - ((modulator_phase >> 7) ^ (modulator_phase >> 2)) & - ((carrier_phase >> 5) ^ (modulator_phase >> 3)) - ) & 1; - } +private: + /*! + @returns The phase bit used for cymbal and high-hat generation, which is a function of two operators' phases. + */ + static constexpr int phase_combination(const int carrier_phase, const int modulator_phase) { + return ( + ((carrier_phase >> 5) ^ (carrier_phase >> 3)) & + ((modulator_phase >> 7) ^ (modulator_phase >> 2)) & + ((carrier_phase >> 5) ^ (modulator_phase >> 3)) + ) & 1; + } }; } diff --git a/Components/OPx/OPLL.cpp b/Components/OPx/OPLL.cpp index b1bb81935..e48128267 100644 --- a/Components/OPx/OPLL.cpp +++ b/Components/OPx/OPLL.cpp @@ -12,7 +12,7 @@ using namespace Yamaha::OPL; -OPLL::OPLL(Concurrency::AsyncTaskQueue &task_queue, int audio_divider, bool is_vrc7): +OPLL::OPLL(Concurrency::AsyncTaskQueue &task_queue, const int audio_divider, const bool is_vrc7): OPLBase(task_queue), audio_divider_(audio_divider), is_vrc7_(is_vrc7) { // Due to the way that sound mixing works on the OPLL, the audio divider may not // be larger than 4. @@ -71,7 +71,7 @@ OPLL::OPLL(Concurrency::AsyncTaskQueue &task_queue, int audio_divider, bo // MARK: - Machine-facing programmatic input. -void OPLL::write_register(uint8_t address, uint8_t value) { +void OPLL::write_register(const uint8_t address, const uint8_t value) { // The OPLL doesn't have timers or other non-audio functions, so all writes // go to the audio queue. task_queue_.enqueue([this, address, value] { @@ -172,7 +172,7 @@ void OPLL::write_register(uint8_t address, uint8_t value) { }); } -void OPLL::set_channel_period(int channel) { +void OPLL::set_channel_period(const int channel) { phase_generators_[channel + 0].set_period(channels_[channel].period, channels_[channel].octave); phase_generators_[channel + 9].set_period(channels_[channel].period, channels_[channel].octave); @@ -183,7 +183,7 @@ void OPLL::set_channel_period(int channel) { key_level_scalers_[channel + 9].set_period(channels_[channel].period, channels_[channel].octave); } -const uint8_t *OPLL::instrument_definition(int instrument, int channel) { +const uint8_t *OPLL::instrument_definition(const int instrument, const int channel) const { // Divert to the appropriate rhythm instrument if in rhythm mode. if(channel >= 6 && rhythm_mode_enabled_) { return &percussion_patch_set[(channel - 6) * 8]; @@ -197,7 +197,7 @@ const uint8_t *OPLL::instrument_definition(int instrument, int channel) { return is_vrc7_ ? &vrc7_patch_set[index] : &opll_patch_set[index]; } -void OPLL::install_instrument(int channel) { +void OPLL::install_instrument(const int channel) { auto &carrier_envelope = envelope_generators_[channel + 0]; auto &carrier_phase = phase_generators_[channel + 0]; auto &carrier_scaler = key_level_scalers_[channel + 0]; @@ -266,7 +266,7 @@ void OPLL::install_instrument(int channel) { carrier_envelope.set_sustain_level(instrument[7] >> 4); } -void OPLL::set_use_sustain(int channel) { +void OPLL::set_use_sustain(const int channel) { const uint8_t *const instrument = instrument_definition(channels_[channel].instrument, channel); envelope_generators_[channel + 0].set_use_sustain_level((instrument[1] & 0x20) || channels_[channel].use_sustain); envelope_generators_[channel + 9].set_use_sustain_level((instrument[0] & 0x20) || channels_[channel].use_sustain); @@ -274,7 +274,7 @@ void OPLL::set_use_sustain(int channel) { // MARK: - Output generation. -void OPLL::set_sample_volume_range(std::int16_t range) { +void OPLL::set_sample_volume_range(const std::int16_t range) { total_volume_ = range; } @@ -387,7 +387,7 @@ void OPLL::update_all_channels() { #define ATTENUATION(x) ((x) << 7) -int OPLL::melodic_output(int channel) { +int OPLL::melodic_output(const int channel) { // The modulator always updates after the carrier, oddly enough. So calculate actual output first, based on the modulator's last value. auto carrier = WaveformGenerator::wave(channels_[channel].carrier_waveform, phase_generators_[channel].scaled_phase(), channels_[channel].modulator_output); carrier += envelope_generators_[channel].attenuation() + ATTENUATION(channels_[channel].attenuation) + key_level_scalers_[channel].attenuation(); @@ -403,7 +403,7 @@ int OPLL::melodic_output(int channel) { return carrier.level(); } -int OPLL::bass_drum() { +int OPLL::bass_drum() const { // Use modulator 6 and carrier 6, attenuated as per the bass-specific envelope generators and the attenuation level for channel 6. auto modulation = WaveformGenerator::wave(Waveform::Sine, phase_generators_[6 + 9].phase()); modulation += rhythm_envelope_generators_[RhythmIndices::BassModulator].attenuation(); @@ -413,7 +413,7 @@ int OPLL::bass_drum() { return carrier.level(); } -int OPLL::tom_tom() { +int OPLL::tom_tom() const { // Use modulator 8 and the 'instrument' selection for channel 8 as an attenuation. auto tom_tom = WaveformGenerator::wave(Waveform::Sine, phase_generators_[8 + 9].phase()); tom_tom += rhythm_envelope_generators_[RhythmIndices::TomTom].attenuation(); @@ -421,7 +421,7 @@ int OPLL::tom_tom() { return tom_tom.level(); } -int OPLL::snare_drum() { +int OPLL::snare_drum() const { // Use modulator 7 and the carrier attenuation level for channel 7. LogSign snare = WaveformGenerator::snare(oscillator_, phase_generators_[7 + 9].phase()); snare += rhythm_envelope_generators_[RhythmIndices::Snare].attenuation(); @@ -429,7 +429,7 @@ int OPLL::snare_drum() { return snare.level(); } -int OPLL::cymbal() { +int OPLL::cymbal() const { // Use modulator 7, carrier 8 and the attenuation level for channel 8. LogSign cymbal = WaveformGenerator::cymbal(phase_generators_[8].phase(), phase_generators_[7 + 9].phase()); cymbal += rhythm_envelope_generators_[RhythmIndices::Cymbal].attenuation(); @@ -437,7 +437,7 @@ int OPLL::cymbal() { return cymbal.level(); } -int OPLL::high_hat() { +int OPLL::high_hat() const { // Use modulator 7, carrier 8 a and the 'instrument' selection for channel 7 as an attenuation. LogSign high_hat = WaveformGenerator::high_hat(oscillator_, phase_generators_[8].phase(), phase_generators_[7 + 9].phase()); high_hat += rhythm_envelope_generators_[RhythmIndices::HighHat].attenuation(); diff --git a/Components/OPx/OPLL.hpp b/Components/OPx/OPLL.hpp index bca92a969..bd7e85c8d 100644 --- a/Components/OPx/OPLL.hpp +++ b/Components/OPx/OPLL.hpp @@ -26,7 +26,7 @@ class OPLL: public OPLBase { /// As per ::SampleSource; provides audio output. template - void apply_samples(std::size_t number_of_samples, Outputs::Speaker::MonoSample *target); + void apply_samples(std::size_t number_of_samples, Outputs::Speaker::MonoSample *); void set_sample_volume_range(std::int16_t range); bool is_zero_level() const { return false; } // TODO. @@ -49,11 +49,11 @@ class OPLL: public OPLBase { void update_all_channels(); int melodic_output(int channel); - int bass_drum(); - int tom_tom(); - int snare_drum(); - int cymbal(); - int high_hat(); + int bass_drum() const; + int tom_tom() const; + int snare_drum() const; + int cymbal() const; + int high_hat() const; static constexpr int period_precision = 9; static constexpr int envelope_precision = 7; @@ -122,7 +122,7 @@ class OPLL: public OPLBase { void set_use_sustain(int channel); /// @returns The 8-byte definition of instrument @c instrument. - const uint8_t *instrument_definition(int instrument, int channel); + const uint8_t *instrument_definition(int instrument, int channel) const; }; } diff --git a/Components/RP5C01/RP5C01.cpp b/Components/RP5C01/RP5C01.cpp index 3caf220b8..562fca6c1 100644 --- a/Components/RP5C01/RP5C01.cpp +++ b/Components/RP5C01/RP5C01.cpp @@ -31,7 +31,7 @@ RP5C01::RP5C01(HalfCycles clock_rate) : clock_rate_(clock_rate) { leap_year_ = time_date->tm_year % 4; } -void RP5C01::run_for(HalfCycles cycles) { +void RP5C01::run_for(const HalfCycles cycles) { sub_seconds_ += cycles; // Guess: this happens so rarely (i.e. once a second, ordinarily) that @@ -96,13 +96,13 @@ void RP5C01::run_for(HalfCycles cycles) { namespace { -constexpr int Reg(int mode, int address) { +constexpr int Reg(const int mode, const int address) { return address | mode << 4; } constexpr int PM = 1 << 4; -constexpr int twenty_four_to_twelve(int hours) { +constexpr int twenty_four_to_twelve(const int hours) { switch(hours) { default: return (hours % 12) + (hours > 12 ? PM : 0); case 0: return 12; diff --git a/Components/RP5C01/RP5C01.hpp b/Components/RP5C01/RP5C01.hpp index 5d0574cf6..8df2fca12 100644 --- a/Components/RP5C01/RP5C01.hpp +++ b/Components/RP5C01/RP5C01.hpp @@ -16,41 +16,41 @@ namespace Ricoh::RP5C01 { class RP5C01 { - public: - RP5C01(HalfCycles clock_rate); +public: + RP5C01(HalfCycles clock_rate); - /// @returns the result of a read from @c address. - uint8_t read(int address); + /// @returns the result of a read from @c address. + uint8_t read(int address); - /// Performs a write of @c value to @c address. - void write(int address, uint8_t value); + /// Performs a write of @c value to @c address. + void write(int address, uint8_t value); - /// Advances time. - void run_for(HalfCycles); + /// Advances time. + void run_for(HalfCycles); - private: - std::array ram_; +private: + std::array ram_; - HalfCycles sub_seconds_; - const HalfCycles clock_rate_; + HalfCycles sub_seconds_; + const HalfCycles clock_rate_; - // Contains the seconds, minutes and hours fields. - int seconds_ = 0; + // Contains the seconds, minutes and hours fields. + int seconds_ = 0; - // Calendar entries. - int day_of_the_week_ = 0; - int day_ = 0; - int month_ = 0; - int year_ = 0; - int leap_year_ = 0; + // Calendar entries. + int day_of_the_week_ = 0; + int day_ = 0; + int month_ = 0; + int year_ = 0; + int leap_year_ = 0; - // Other flags. - bool timer_enabled_ = false; - bool alarm_enabled_ = false; - int mode_ = 0; - bool one_hz_on_ = false; - bool sixteen_hz_on_ = false; - bool twentyfour_hour_clock_ = true; + // Other flags. + bool timer_enabled_ = false; + bool alarm_enabled_ = false; + int mode_ = 0; + bool one_hz_on_ = false; + bool sixteen_hz_on_ = false; + bool twentyfour_hour_clock_ = true; }; } diff --git a/Components/SN76489/SN76489.cpp b/Components/SN76489/SN76489.cpp index 007df3516..4b8ff4fda 100644 --- a/Components/SN76489/SN76489.cpp +++ b/Components/SN76489/SN76489.cpp @@ -13,7 +13,11 @@ using namespace TI; -SN76489::SN76489(Personality personality, Concurrency::AsyncTaskQueue &task_queue, int additional_divider) : task_queue_(task_queue) { +SN76489::SN76489( + const Personality personality, + Concurrency::AsyncTaskQueue &task_queue, + const int additional_divider +) : task_queue_(task_queue) { set_sample_volume_range(0); switch(personality) { @@ -36,7 +40,7 @@ SN76489::SN76489(Personality personality, Concurrency::AsyncTaskQueue &ta master_divider_period_ /= additional_divider; } -void SN76489::set_sample_volume_range(std::int16_t range) { +void SN76489::set_sample_volume_range(const std::int16_t range) { // Build a volume table. double multiplier = pow(10.0, -0.1); double volume = float(range) / 4.0f; // As there are four channels. @@ -48,7 +52,7 @@ void SN76489::set_sample_volume_range(std::int16_t range) { evaluate_output_volume(); } -void SN76489::write(uint8_t value) { +void SN76489::write(const uint8_t value) { task_queue_.enqueue([value, this] () { if(value & 0x80) { active_register_ = value; @@ -87,7 +91,11 @@ void SN76489::write(uint8_t value) { } bool SN76489::is_zero_level() const { - return channels_[0].volume == 0xf && channels_[1].volume == 0xf && channels_[2].volume == 0xf && channels_[3].volume == 0xf; + return + channels_[0].volume == 0xf && + channels_[1].volume == 0xf && + channels_[2].volume == 0xf && + channels_[3].volume == 0xf; } void SN76489::evaluate_output_volume() { @@ -100,7 +108,7 @@ void SN76489::evaluate_output_volume() { } template -void SN76489::apply_samples(std::size_t number_of_samples, Outputs::Speaker::MonoSample *target) { +void SN76489::apply_samples(const std::size_t number_of_samples, Outputs::Speaker::MonoSample *const target) { std::size_t c = 0; while((master_divider_& (master_divider_period_ - 1)) && c < number_of_samples) { Outputs::Speaker::apply(target[c], output_volume_); diff --git a/Components/SN76489/SN76489.hpp b/Components/SN76489/SN76489.hpp index 5e79e3e83..926ed8de4 100644 --- a/Components/SN76489/SN76489.hpp +++ b/Components/SN76489/SN76489.hpp @@ -14,53 +14,53 @@ namespace TI { class SN76489: public Outputs::Speaker::BufferSource { - public: - enum class Personality { - SN76489, - SN76494, - SMS - }; +public: + enum class Personality { + SN76489, + SN76494, + SMS + }; - /// Creates a new SN76489. - SN76489(Personality personality, Concurrency::AsyncTaskQueue &task_queue, int additional_divider = 1); + /// Creates a new SN76489. + SN76489(Personality, Concurrency::AsyncTaskQueue &, int additional_divider = 1); - /// Writes a new value to the SN76489. - void write(uint8_t value); + /// Writes a new value to the SN76489. + void write(uint8_t); - // As per SampleSource. - template - void apply_samples(std::size_t number_of_samples, Outputs::Speaker::MonoSample *target); - bool is_zero_level() const; - void set_sample_volume_range(std::int16_t range); + // As per SampleSource. + template + void apply_samples(std::size_t number_of_samples, Outputs::Speaker::MonoSample *target); + bool is_zero_level() const; + void set_sample_volume_range(std::int16_t range); - private: - int master_divider_ = 0; - int master_divider_period_ = 16; - int16_t output_volume_ = 0; - void evaluate_output_volume(); - int volumes_[16]; +private: + int master_divider_ = 0; + int master_divider_period_ = 16; + int16_t output_volume_ = 0; + void evaluate_output_volume(); + int volumes_[16]; - Concurrency::AsyncTaskQueue &task_queue_; + Concurrency::AsyncTaskQueue &task_queue_; - struct ToneChannel { - // Programmatically-set state; updated by the processor. - uint16_t divider = 0; - uint8_t volume = 0xf; + struct ToneChannel { + // Programmatically-set state; updated by the processor. + uint16_t divider = 0; + uint8_t volume = 0xf; - // Active state; self-evolving as a function of time. - uint16_t counter = 0; - int level = 0; - } channels_[4]; - enum { - Periodic15, - Periodic16, - Noise15, - Noise16 - } noise_mode_ = Periodic15; - uint16_t noise_shifter_ = 0; - int active_register_ = 0; + // Active state; self-evolving as a function of time. + uint16_t counter = 0; + int level = 0; + } channels_[4]; + enum { + Periodic15, + Periodic16, + Noise15, + Noise16 + } noise_mode_ = Periodic15; + uint16_t noise_shifter_ = 0; + int active_register_ = 0; - bool shifter_is_16bit_ = false; + bool shifter_is_16bit_ = false; }; } diff --git a/Components/Serial/Line.cpp b/Components/Serial/Line.cpp index 6caf71a7c..c8c923a2d 100644 --- a/Components/Serial/Line.cpp +++ b/Components/Serial/Line.cpp @@ -14,7 +14,7 @@ using namespace Serial; template -void Line::set_writer_clock_rate(HalfCycles clock_rate) { +void Line::set_writer_clock_rate(const HalfCycles clock_rate) { clock_rate_ = clock_rate; } @@ -73,7 +73,7 @@ void Line::advance_writer(HalfCycles cycles) { } template -void Line::write(bool level) { +void Line::write(const bool level) { if(!events_.empty()) { events_.emplace_back(); events_.back().type = level ? Event::SetHigh : Event::SetLow; @@ -84,7 +84,11 @@ void Line::write(bool level) { } template -template void Line::write_internal(HalfCycles cycles, int count, IntT levels) { +template void Line::write_internal( + const HalfCycles cycles, + int count, + const IntT levels +) { remaining_delays_ += count * cycles.as_integral(); auto event = events_.size(); @@ -108,12 +112,12 @@ template void Line::write_interna } template -void Line::write(HalfCycles cycles, int count, int levels) { +void Line::write(const HalfCycles cycles, const int count, const int levels) { write_internal(cycles, count, levels); } template -template void Line::write(HalfCycles cycles, IntT value) { +template void Line::write(const HalfCycles cycles, const IntT value) { write_internal(cycles, 8 * sizeof(IntT), value); } @@ -129,7 +133,10 @@ bool Line::read() const { } template -void Line::set_read_delegate(ReadDelegate *delegate, [[maybe_unused]] Storage::Time bit_length) { +void Line::set_read_delegate( + ReadDelegate *const delegate, + [[maybe_unused]] const Storage::Time bit_length +) { read_delegate_ = delegate; if constexpr (!include_clock) { assert(bit_length > Storage::Time(0)); @@ -140,7 +147,7 @@ void Line::set_read_delegate(ReadDelegate *delegate, [[maybe_unus } template -void Line::update_delegate(bool level) { +void Line::update_delegate(const bool level) { // Exit early if there's no delegate, or if the delegate is waiting for // zero and this isn't zero. if(!read_delegate_) return; diff --git a/Components/Serial/Line.hpp b/Components/Serial/Line.hpp index 483815c29..989442cb8 100644 --- a/Components/Serial/Line.hpp +++ b/Components/Serial/Line.hpp @@ -41,97 +41,96 @@ namespace Serial { all enqueued bits at appropriate times. */ template class Line { - public: - /// Sets the line to @c level instantaneously. - void write(bool level); +public: + /// Sets the line to @c level instantaneously. + void write(bool); - /// @returns The instantaneous level of this line. - bool read() const; + /// @returns The instantaneous level of this line. + bool read() const; - /// Sets the denominator for the between levels for any data enqueued - /// via an @c write. - void set_writer_clock_rate(HalfCycles clock_rate); + /// Sets the denominator for the between levels for any data enqueued + /// via an @c write. + void set_writer_clock_rate(HalfCycles); - /// Enqueues @c count level changes, the first occurring immediately - /// after the final event currently posted and each subsequent event - /// occurring @c cycles after the previous. An additional gap of @c cycles - /// is scheduled after the final output. The levels to output are - /// taken from @c levels which is read from lsb to msb. @c cycles is - /// relative to the writer's clock rate. - void write(HalfCycles cycles, int count, int levels); + /// Enqueues @c count level changes, the first occurring immediately + /// after the final event currently posted and each subsequent event + /// occurring @c cycles after the previous. An additional gap of @c cycles + /// is scheduled after the final output. The levels to output are + /// taken from @c levels which is read from lsb to msb. @c cycles is + /// relative to the writer's clock rate. + void write(HalfCycles cycles, int count, int levels); - /// Enqueus every bit from @c value as per the rules of write(HalfCycles, int, int), - /// either in LSB or MSB order as per the @c lsb_first template flag. - template void write(HalfCycles cycles, IntT value); + /// Enqueus every bit from @c value as per the rules of write(HalfCycles, int, int), + /// either in LSB or MSB order as per the @c lsb_first template flag. + template void write(HalfCycles cycles, IntT value); - /// @returns the number of cycles until currently enqueued write data is exhausted. - forceinline HalfCycles write_data_time_remaining() const { - return HalfCycles(remaining_delays_); - } + /// @returns the number of cycles until currently enqueued write data is exhausted. + forceinline HalfCycles write_data_time_remaining() const { + return HalfCycles(remaining_delays_); + } - /// @returns the number of cycles left until it is guaranteed that a passive reader - /// has received all currently-enqueued bits. - forceinline HalfCycles transmission_data_time_remaining() const { - return HalfCycles(remaining_delays_ + transmission_extra_); - } + /// @returns the number of cycles left until it is guaranteed that a passive reader + /// has received all currently-enqueued bits. + forceinline HalfCycles transmission_data_time_remaining() const { + return HalfCycles(remaining_delays_ + transmission_extra_); + } - /// Advances the read position by @c cycles relative to the writer's - /// clock rate. - void advance_writer(HalfCycles cycles); + /// Advances the read position relative to the writer's clock rate. + void advance_writer(HalfCycles); - /// Eliminates all future write states, leaving the output at whatever it is now. - void reset_writing(); + /// Eliminates all future write states, leaving the output at whatever it is now. + void reset_writing(); - struct ReadDelegate { - virtual bool serial_line_did_produce_bit(Line *line, int bit) = 0; - }; - /*! - Sets a read delegate. + struct ReadDelegate { + virtual bool serial_line_did_produce_bit(Line *, int bit) = 0; + }; + /*! + Sets a read delegate. - Single line serial connection: + Single line serial connection: - The delegate will receive samples of the output level every - @c bit_lengths of a second apart subject to a state machine: + The delegate will receive samples of the output level every + @c bit_lengths of a second apart subject to a state machine: - * initially no bits will be delivered; - * when a zero level is first detected, the line will wait half a bit's length, then start - sampling at single-bit intervals, passing each bit to the delegate while it returns @c true; - * as soon as the delegate returns @c false, the line will return to the initial state. + * initially no bits will be delivered; + * when a zero level is first detected, the line will wait half a bit's length, then start + sampling at single-bit intervals, passing each bit to the delegate while it returns @c true; + * as soon as the delegate returns @c false, the line will return to the initial state. - Two-line clock + data connection: + Two-line clock + data connection: - The delegate will receive every bit that has been enqueued, spaced as nominated - by the writer. @c bit_length is ignored, as is the return result of - @c ReadDelegate::serial_line_did_produce_bit. - */ - void set_read_delegate(ReadDelegate *delegate, Storage::Time bit_length = Storage::Time()); + The delegate will receive every bit that has been enqueued, spaced as nominated + by the writer. @c bit_length is ignored, as is the return result of + @c ReadDelegate::serial_line_did_produce_bit. + */ + void set_read_delegate(ReadDelegate *, Storage::Time bit_length = Storage::Time()); - private: - struct Event { - enum Type { - Delay, SetHigh, SetLow - } type; - int delay; - }; - std::vector events_; - HalfCycles::IntType remaining_delays_ = 0; - HalfCycles::IntType transmission_extra_ = 0; - bool level_ = true; - HalfCycles clock_rate_ = 0; +private: + struct Event { + enum Type { + Delay, SetHigh, SetLow + } type; + int delay; + }; + std::vector events_; + HalfCycles::IntType remaining_delays_ = 0; + HalfCycles::IntType transmission_extra_ = 0; + bool level_ = true; + HalfCycles clock_rate_ = 0; - ReadDelegate *read_delegate_ = nullptr; - Storage::Time read_delegate_bit_length_, time_left_in_bit_; - int write_cycles_since_delegate_call_ = 0; - enum class ReadDelegatePhase { - WaitingForZero, - Serialising - } read_delegate_phase_ = ReadDelegatePhase::WaitingForZero; + ReadDelegate *read_delegate_ = nullptr; + Storage::Time read_delegate_bit_length_, time_left_in_bit_; + int write_cycles_since_delegate_call_ = 0; + enum class ReadDelegatePhase { + WaitingForZero, + Serialising + } read_delegate_phase_ = ReadDelegatePhase::WaitingForZero; - void update_delegate(bool level); - HalfCycles::IntType minimum_write_cycles_for_read_delegate_bit(); + void update_delegate(bool level); + HalfCycles::IntType minimum_write_cycles_for_read_delegate_bit(); - template void - write_internal(HalfCycles, int, IntT); + template void + write_internal(HalfCycles, int, IntT); }; /*!