1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-11-22 12:33:29 +00:00

Splits out the queue management stuff from queue+action.

Temporarily breaks ST video in the endeavour.
This commit is contained in:
Thomas Harte 2020-01-29 22:18:41 -05:00
parent 8c4fb0f688
commit f0a6e0f3d5
4 changed files with 88 additions and 54 deletions

View File

@ -13,17 +13,10 @@
#include <vector> #include <vector>
/*! /*!
A DeferredQueue maintains a list of ordered actions and the times at which Provides the logic to insert into and traverse a list of future scheduled items.
they should happen, and divides a total execution period up into the portions
that occur between those actions, triggering each action when it is reached.
This list is efficient only for short queues.
*/ */
template <typename TimeUnit> class DeferredQueue { template <typename TimeUnit> class DeferredQueue {
public: public:
/// Constructs a DeferredQueue that will call target(period) in between deferred actions.
DeferredQueue(std::function<void(TimeUnit)> &&target) : target_(std::move(target)) {}
/*! /*!
Schedules @c action to occur in @c delay units of time. Schedules @c action to occur in @c delay units of time.
*/ */
@ -53,26 +46,23 @@ template <typename TimeUnit> class DeferredQueue {
} }
/*! /*!
Runs for @c length units of time. @returns The amount of time until the next enqueued action will occur,
or TimeUnit(-1) if the queue is empty.
The constructor-supplied target will be called with one or more periods that add up to @c length;
any scheduled actions will be called between periods.
*/ */
void run_for(TimeUnit length) { TimeUnit time_until_next_action() {
// If there are no pending actions, just run for the entire length. if(pending_actions_.empty()) return TimeUnit(-1);
// This should be the normal branch. return pending_actions_.front().delay;
if(pending_actions_.empty()) { }
target_(length);
return;
}
// Divide the time to run according to the pending actions. /*!
Advances the queue the specified amount of time, performing any actions it reaches.
*/
void advance(TimeUnit time) {
auto erase_iterator = pending_actions_.begin(); auto erase_iterator = pending_actions_.begin();
while(erase_iterator != pending_actions_.end()) { while(erase_iterator != pending_actions_.end()) {
erase_iterator->delay -= length; erase_iterator->delay -= time;
if(erase_iterator->delay <= TimeUnit(0)) { if(erase_iterator->delay <= TimeUnit(0)) {
target_(length + erase_iterator->delay); time = -erase_iterator->delay;
length = -erase_iterator->delay;
erase_iterator->action(); erase_iterator->action();
++erase_iterator; ++erase_iterator;
} else { } else {
@ -82,14 +72,23 @@ template <typename TimeUnit> class DeferredQueue {
if(erase_iterator != pending_actions_.begin()) { if(erase_iterator != pending_actions_.begin()) {
pending_actions_.erase(pending_actions_.begin(), erase_iterator); pending_actions_.erase(pending_actions_.begin(), erase_iterator);
} }
if(length != TimeUnit(0)) { }
target_(length);
/*!
Advances the queue by @c min(time_until_next_action(),duration) time.
*/
void advance_to_next(TimeUnit duration) {
if(pending_actions_.empty()) return;
auto front = pending_actions_.front();
front.delay -= duration;
if(front.delay <= TimeUnit(0)) {
front.action();
pending_actions_.erase(pending_actions_.begin());
} }
} }
private: private:
std::function<void(TimeUnit)> target_;
// The list of deferred actions. // The list of deferred actions.
struct DeferredAction { struct DeferredAction {
TimeUnit delay; TimeUnit delay;
@ -100,4 +99,38 @@ template <typename TimeUnit> class DeferredQueue {
std::vector<DeferredAction> pending_actions_; std::vector<DeferredAction> pending_actions_;
}; };
/*!
A DeferredQueue maintains a list of ordered actions and the times at which
they should happen, and divides a total execution period up into the portions
that occur between those actions, triggering each action when it is reached.
This list is efficient only for short queues.
*/
template <typename TimeUnit> class DeferredQueuePerformer: public DeferredQueue<TimeUnit> {
public:
/// Constructs a DeferredQueue that will call target(period) in between deferred actions.
DeferredQueuePerformer(std::function<void(TimeUnit)> &&target) : target_(std::move(target)) {}
/*!
Runs for @c length units of time.
The constructor-supplied target will be called with one or more periods that add up to @c length;
any scheduled actions will be called between periods.
*/
void run_for(TimeUnit length) {
auto time_to_next = DeferredQueue<TimeUnit>::time_until_next_action();
while(time_to_next != TimeUnit(-1) && time_to_next <= length) {
target_(time_to_next);
length -= time_to_next;
DeferredQueue<TimeUnit>::advance_to_next(time_to_next);
}
DeferredQueue<TimeUnit>::advance_to_next(length);
target_(length);
}
private:
std::function<void(TimeUnit)> target_;
};
#endif /* DeferredQueue_h */ #endif /* DeferredQueue_h */

View File

@ -255,7 +255,7 @@ class VideoBase {
void output_fat_low_resolution(uint8_t *target, const uint8_t *source, size_t length, int column, int row) const; void output_fat_low_resolution(uint8_t *target, const uint8_t *source, size_t length, int column, int row) const;
// Maintain a DeferredQueue for delayed mode switches. // Maintain a DeferredQueue for delayed mode switches.
DeferredQueue<Cycles> deferrer_; DeferredQueuePerformer<Cycles> deferrer_;
}; };
template <class BusHandler, bool is_iie> class Video: public VideoBase { template <class BusHandler, bool is_iie> class Video: public VideoBase {

View File

@ -327,7 +327,7 @@ void Video::advance(HalfCycles duration) {
const bool next_display_enable = vertical_.enable && horizontal_.enable; const bool next_display_enable = vertical_.enable && horizontal_.enable;
if(display_enable != next_display_enable) { if(display_enable != next_display_enable) {
// Schedule change in outwardly-visible DE line. // Schedule change in outwardly-visible DE line.
add_event(de_delay_period - integer_duration, next_display_enable ? Event::Type::SetDisplayEnable : Event::Type::ResetDisplayEnable); // add_event(de_delay_period - integer_duration, next_display_enable ? Event::Type::SetDisplayEnable : Event::Type::ResetDisplayEnable);
// Schedule change in inwardly-visible effect. // Schedule change in inwardly-visible effect.
next_load_toggle_ = x_ + 8; // 4 cycles = 8 half-cycles next_load_toggle_ = x_ + 8; // 4 cycles = 8 half-cycles
@ -335,35 +335,35 @@ void Video::advance(HalfCycles duration) {
if(horizontal_.sync != hsync) { if(horizontal_.sync != hsync) {
// Schedule change in outwardly-visible hsync line. // Schedule change in outwardly-visible hsync line.
add_event(hsync_delay_period - integer_duration, horizontal_.sync ? Event::Type::SetHsync : Event::Type::ResetHsync); // add_event(hsync_delay_period - integer_duration, horizontal_.sync ? Event::Type::SetHsync : Event::Type::ResetHsync);
} }
if(vertical_.sync != vsync) { if(vertical_.sync != vsync) {
// Schedule change in outwardly-visible hsync line. // Schedule change in outwardly-visible hsync line.
add_event(vsync_delay_period - integer_duration, vertical_.sync ? Event::Type::SetVsync : Event::Type::ResetVsync); // add_event(vsync_delay_period - integer_duration, vertical_.sync ? Event::Type::SetVsync : Event::Type::ResetVsync);
} }
} }
// Effect any changes in visible state out here; they've been supplied as sequence points, so // Effect any changes in visible state out here; they've been supplied as sequence points, so
// a conforming caller can't hit them within the inner loop. // a conforming caller can't hit them within the inner loop.
integer_duration = int(duration.as_integral()); // integer_duration = int(duration.as_integral());
if(!pending_events_.empty()) { // if(!pending_events_.empty()) {
auto erase_iterator = pending_events_.begin(); // auto erase_iterator = pending_events_.begin();
int duration_remaining = integer_duration; // int duration_remaining = integer_duration;
while(erase_iterator != pending_events_.end()) { // while(erase_iterator != pending_events_.end()) {
erase_iterator->delay -= duration_remaining; // erase_iterator->delay -= duration_remaining;
if(erase_iterator->delay <= 0) { // if(erase_iterator->delay <= 0) {
duration_remaining = -erase_iterator->delay; // duration_remaining = -erase_iterator->delay;
erase_iterator->apply(public_state_); // erase_iterator->apply(public_state_);
++erase_iterator; // ++erase_iterator;
} else { // } else {
break; // break;
} // }
} // }
if(erase_iterator != pending_events_.begin()) { // if(erase_iterator != pending_events_.begin()) {
pending_events_.erase(pending_events_.begin(), erase_iterator); // pending_events_.erase(pending_events_.begin(), erase_iterator);
} // }
} // }
} }
void Video::push_latched_data() { void Video::push_latched_data() {
@ -417,8 +417,9 @@ HalfCycles Video::get_next_sequence_point() {
int event_time = line_length_.length; // Worst case: report end of line. int event_time = line_length_.length; // Worst case: report end of line.
// If any events are pending, give the first of those the chance to be next. // If any events are pending, give the first of those the chance to be next.
if(!pending_events_.empty()) { const auto next_deferred_item = deferrer_.time_until_next_action();
event_time = std::min(event_time, x_ + pending_events_.front().delay); if(next_deferred_item != HalfCycles(-1)) {
event_time = std::min(event_time, next_deferred_item.as<int>());
} }
// If this is a vertically-enabled line, check for the display enable boundaries, + the standard delay. // If this is a vertically-enabled line, check for the display enable boundaries, + the standard delay.

View File

@ -122,7 +122,7 @@ class Video {
private: private:
void advance(HalfCycles duration); void advance(HalfCycles duration);
DeferredQueue<HalfCycles> deferrer_; DeferredQueuePerformer<HalfCycles> deferrer_;
Outputs::CRT::CRT crt_; Outputs::CRT::CRT crt_;
RangeObserver *range_observer_ = nullptr; RangeObserver *range_observer_ = nullptr;
@ -270,7 +270,7 @@ class Video {
} }
}; };
std::vector<Event> pending_events_; /* std::vector<Event> pending_events_;
void add_event(int delay, Event::Type type) { void add_event(int delay, Event::Type type) {
// Apply immediately if there's no delay (or a negative delay). // Apply immediately if there's no delay (or a negative delay).
if(delay <= 0) { if(delay <= 0) {
@ -294,7 +294,7 @@ class Video {
} else { } else {
pending_events_.emplace_back(type, delay); pending_events_.emplace_back(type, delay);
} }
} }*/
friend class ::VideoTester; friend class ::VideoTester;
}; };