diff --git a/Machines/Amiga/Blitter.hpp b/Machines/Amiga/Blitter.hpp index fc88261b3..38dcd9309 100644 --- a/Machines/Amiga/Blitter.hpp +++ b/Machines/Amiga/Blitter.hpp @@ -49,40 +49,38 @@ class BlitterSequencer { /// Sets the current control value, which indicates which /// channels are enabled. void set_control(int control) { - control_ = control; - index_ = 0; + 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() { - phase_ = + next_phase_ = (control_ == 0x9 || control_ == 0xb || control_ == 0xd) ? Phase::PauseAndComplete : Phase::Complete; } /// Begins a blit operation. void begin() { - phase_ = Phase::Ongoing; - index_ = 0; + phase_ = next_phase_ = Phase::Ongoing; + index_ = loop_ = 0; } - /// Provides the next channel to fetch from, or that a write is required. - Channel next() { + /// 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: - if(!index_) return Channel::Write; - break; + return std::make_pair(Channel::Write, loop_); case Phase::PauseAndComplete: - if(!index_) { - phase_ = Phase::Complete; - return Channel::None; - } - break; + phase_ = Phase::Complete; + return std::make_pair(Channel::None, loop_); } Channel next = Channel::None; @@ -107,7 +105,7 @@ class BlitterSequencer { case 15: next = next_channel(patternF); break; } - return next; + return std::make_pair(next, loop_); } private: @@ -127,16 +125,40 @@ class BlitterSequencer { 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_ = (index_ + 1) % list.size(); + ++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 { - Ongoing, PauseAndComplete, Complete - } phase_ = Phase::Complete; + /// 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; }; class Blitter: public DMADevice<4, 4> { diff --git a/OSBindings/Mac/Clock SignalTests/AmigaBlitterTests.mm b/OSBindings/Mac/Clock SignalTests/AmigaBlitterTests.mm index 9e10cad7a..159bb7cc7 100644 --- a/OSBindings/Mac/Clock SignalTests/AmigaBlitterTests.mm +++ b/OSBindings/Mac/Clock SignalTests/AmigaBlitterTests.mm @@ -294,13 +294,12 @@ using WriteVector = std::vector>; /* F */ @"A0 B0 C0 - A1 B1 C1 D0 A2 B2 C2 D1 D2", ]; - for(int c = 0; c < 16; c++) { + for(int c = 0; c < 16; c++) { Amiga::BlitterSequencer sequencer; sequencer.set_control(c); sequencer.begin(); - int counts[4]{}; - const int writes = 2; + int writes = 0; NSUInteger length = [[patterns[c] componentsSeparatedByString:@" "] count]; bool is_first_write = c > 1; // control = 1 is D only, in which case don't pipeline. NSMutableArray *const components = [[NSMutableArray alloc] init]; @@ -309,24 +308,27 @@ using WriteVector = std::vector>; const auto next = sequencer.next(); using Channel = Amiga::BlitterSequencer::Channel; - switch(next) { + switch(next.first) { case Channel::None: [components addObject:@"-"]; break; - case Channel::A: [components addObject:[NSString stringWithFormat:@"A%d", counts[0]++]]; break; - case Channel::B: [components addObject:[NSString stringWithFormat:@"B%d", counts[1]++]]; break; - case Channel::C: [components addObject:[NSString stringWithFormat:@"C%d", counts[2]++]]; break; + case Channel::A: [components addObject:[NSString stringWithFormat:@"A%d", next.second]]; break; + case Channel::B: [components addObject:[NSString stringWithFormat:@"B%d", next.second]]; break; + case Channel::C: [components addObject:[NSString stringWithFormat:@"C%d", next.second]]; break; case Channel::Write: if(is_first_write) { is_first_write = false; [components addObject:@"-"]; } else { - [components addObject:[NSString stringWithFormat:@"D%d", counts[3]++]]; - if(counts[3] == writes) sequencer.complete(); + [components addObject:[NSString stringWithFormat:@"D%d", writes++]]; } break; default: break; } + + if(next.second == 2) { + sequencer.complete(); + } } NSString *pattern = [components componentsJoinedByString:@" "];