// // BlitterSequencer.hpp // Clock Signal // // Created by Thomas Harte on 19/08/2022. // Copyright © 2022 Thomas Harte. All rights reserved. // #pragma once #include namespace Amiga { /*! Statefully provides the next access the Blitter should make. TODO: determine the actual logic here, rather than relying on tables. */ class BlitterSequencer { public: enum class Channel { /// Tells the caller to calculate and load a new piece of output /// into the output pipeline. /// /// If any inputs are enabled then a one-slot output pipeline applies: /// output will rest in the pipeline for one write phase before being written. Write, /// Indicates that a write should occur if anything is in the pipeline, otherwise /// no activity should occur. FlushPipeline, /// The caller should read from channel C. C, /// The caller should read from channel B. B, /// The caller should read from channel A. A, /// Indicates an unused DMA slot. None }; /// Sets the current control value, which indicates which /// channels are enabled. void set_control(int control) { control_ = control & 0xf; index_ = 0; // TODO: this probably isn't accurate; case caught is a change // of control values during a blit. } /// Indicates that blitting should conclude after this step, i.e. /// whatever is being fetched now is part of the final set of input data; /// this is safe to call following a fetch request on any channel. void complete() { next_phase_ = (control_ == 0x9 || control_ == 0xb || control_ == 0xd) ? Phase::PauseAndComplete : Phase::Complete; } /// Begins a blit operation. void begin() { phase_ = next_phase_ = Phase::Ongoing; index_ = loop_ = 0; } /// Provides the next channel to fetch from, or that a write is required, /// along with a count of complete channel iterations so far completed. std::pair next() { switch(phase_) { default: break; case Phase::Complete: return std::make_pair(Channel::FlushPipeline, loop_); case Phase::PauseAndComplete: phase_ = Phase::Complete; return std::make_pair(Channel::None, loop_); } Channel next = Channel::None; switch(control_) { default: break; case 0: next = next_channel(pattern0); break; case 1: next = next_channel(pattern1); break; case 2: next = next_channel(pattern2); break; case 3: next = next_channel(pattern3); break; case 4: next = next_channel(pattern4); break; case 5: next = next_channel(pattern5); break; case 6: next = next_channel(pattern6); break; case 7: next = next_channel(pattern7); break; case 8: next = next_channel(pattern8); break; case 9: next = next_channel(pattern9); break; case 10: next = next_channel(patternA); break; case 11: next = next_channel(patternB); break; case 12: next = next_channel(patternC); break; case 13: next = next_channel(patternD); break; case 14: next = next_channel(patternE); break; case 15: next = next_channel(patternF); break; } return std::make_pair(next, loop_); } template bool channel_enabled() { return control_ & (8 >> channel); } private: static constexpr std::array pattern0 = { Channel::None }; static constexpr std::array pattern1 = { Channel::Write, Channel::None }; static constexpr std::array pattern2 = { Channel::C, Channel::None }; static constexpr std::array pattern3 = { Channel::C, Channel::Write, Channel::None }; static constexpr std::array pattern4 = { Channel::B, Channel::None, Channel::None }; static constexpr std::array pattern5 = { Channel::B, Channel::Write, Channel::None }; static constexpr std::array pattern6 = { Channel::B, Channel::C, Channel::None }; static constexpr std::array pattern7 = { Channel::B, Channel::C, Channel::Write, Channel::None }; static constexpr std::array pattern8 = { Channel::A, Channel::None }; static constexpr std::array pattern9 = { Channel::A, Channel::Write }; static constexpr std::array patternA = { Channel::A, Channel::C }; static constexpr std::array patternB = { Channel::A, Channel::C, Channel::Write }; static constexpr std::array patternC = { Channel::A, Channel::B, Channel::None }; static constexpr std::array patternD = { Channel::A, Channel::B, Channel::Write }; static constexpr std::array patternE = { Channel::A, Channel::B, Channel::C }; static constexpr std::array patternF = { Channel::A, Channel::B, Channel::C, Channel::Write }; template Channel next_channel(const ArrayT &list) { loop_ += index_ / list.size(); index_ %= list.size(); const Channel result = list[index_]; ++index_; if(index_ == list.size()) { phase_ = next_phase_; } return result; } // Current control flags, i.e. which channels are enabled. int control_ = 0; // Index into the pattern table for this blit. size_t index_ = 0; // Number of times the entire pattern table has been completed. int loop_ = 0; enum class Phase { /// Return the next thing in the pattern table and advance. /// If looping from the end of the pattern table to the start, /// set phase_ to next_phase_. Ongoing, /// Return a Channel::None and advancce to phase_ = Phase::Complete. PauseAndComplete, /// Return Channel::Write indefinitely. Complete }; // Current sequencer pahse. Phase phase_ = Phase::Complete; // Phase to assume at the end of this iteration of the sequence table. Phase next_phase_ = Phase::Complete; }; }