// // Mockingboard.hpp // Clock Signal // // Created by Thomas Harte on 14/02/2024. // Copyright © 2024 Thomas Harte. All rights reserved. // #pragma once #include "Card.hpp" #include "../../../Components/6522/6522.hpp" namespace Apple::II { class AYPair { public: AYPair(Concurrency::AsyncTaskQueue &queue) : ays_{ {GI::AY38910::Personality::AY38910, queue}, {GI::AY38910::Personality::AY38910, queue}, } {} void advance() { ays_[0].advance(); ays_[1].advance(); } void set_sample_volume_range(std::int16_t range) { ays_[0].set_sample_volume_range(range); ays_[1].set_sample_volume_range(range); } bool is_zero_level() const { return ays_[0].is_zero_level() && ays_[1].is_zero_level(); } Outputs::Speaker::StereoSample level() const { Outputs::Speaker::StereoSample level; level.left = ays_[0].level(); level.right = ays_[1].level(); return level; } GI::AY38910::AY38910SampleSource &get(int index) { return ays_[index]; } private: GI::AY38910::AY38910SampleSource ays_[2]; }; class Mockingboard: public Card { public: Mockingboard(AYPair &ays) : vias_{ {handlers_[0]}, {handlers_[1]} }, handlers_{ {*this, ays.get(0)}, {*this, ays.get(1)}} { set_select_constraints(0); } void perform_bus_operation(Select select, bool is_read, uint16_t address, uint8_t *value) final { if(!(select & IO)) { return; } int index = (address >> 7) & 1; if(is_read) { *value = vias_[index].read(address); } else { vias_[index].write(address, *value); } } void run_for(Cycles cycles, int) final { vias_[0].run_for(cycles); vias_[1].run_for(cycles); } bool nmi() final { return handlers_[1].interrupt; } bool irq() final { return handlers_[0].interrupt; } void did_change_interrupt_flags() { delegate_->card_did_change_interrupt_flags(this); } private: struct AYVIA: public MOS::MOS6522::PortHandler { AYVIA(Mockingboard &card, GI::AY38910::AY38910SampleSource &ay) : card(card), ay(ay) {} void set_interrupt_status(bool status) { interrupt = status; card.did_change_interrupt_flags(); } void set_port_output(MOS::MOS6522::Port port, uint8_t value, uint8_t) { if(port) { using ControlLines = GI::AY38910::ControlLines; ay.set_control_lines( ControlLines( ((value & 1) ? ControlLines::BC1 : 0) | ((value & 2) ? ControlLines::BDIR : 0) | ControlLines::BC2 ) ); ay.set_reset(!(value & 4)); } else { ay.set_data_input(value); } } uint8_t get_port_input(MOS::MOS6522::Port port) { if(!port) { return ay.get_data_output(); } return 0xff; } bool interrupt; Mockingboard &card; GI::AY38910::AY38910SampleSource &ay; }; MOS::MOS6522::MOS6522 vias_[2]; AYVIA handlers_[2]; }; }