1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-11-15 12:08:33 +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>
/*!
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.
Provides the logic to insert into and traverse a list of future scheduled items.
*/
template <typename TimeUnit> class DeferredQueue {
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.
*/
@ -53,26 +46,23 @@ template <typename TimeUnit> class DeferredQueue {
}
/*!
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.
@returns The amount of time until the next enqueued action will occur,
or TimeUnit(-1) if the queue is empty.
*/
void run_for(TimeUnit length) {
// If there are no pending actions, just run for the entire length.
// This should be the normal branch.
if(pending_actions_.empty()) {
target_(length);
return;
TimeUnit time_until_next_action() {
if(pending_actions_.empty()) return TimeUnit(-1);
return pending_actions_.front().delay;
}
// 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();
while(erase_iterator != pending_actions_.end()) {
erase_iterator->delay -= length;
erase_iterator->delay -= time;
if(erase_iterator->delay <= TimeUnit(0)) {
target_(length + erase_iterator->delay);
length = -erase_iterator->delay;
time = -erase_iterator->delay;
erase_iterator->action();
++erase_iterator;
} else {
@ -82,14 +72,23 @@ template <typename TimeUnit> class DeferredQueue {
if(erase_iterator != pending_actions_.begin()) {
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:
std::function<void(TimeUnit)> target_;
// The list of deferred actions.
struct DeferredAction {
TimeUnit delay;
@ -100,4 +99,38 @@ template <typename TimeUnit> class DeferredQueue {
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 */

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;
// Maintain a DeferredQueue for delayed mode switches.
DeferredQueue<Cycles> deferrer_;
DeferredQueuePerformer<Cycles> deferrer_;
};
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;
if(display_enable != next_display_enable) {
// 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.
next_load_toggle_ = x_ + 8; // 4 cycles = 8 half-cycles
@ -335,35 +335,35 @@ void Video::advance(HalfCycles duration) {
if(horizontal_.sync != hsync) {
// 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) {
// 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
// a conforming caller can't hit them within the inner loop.
integer_duration = int(duration.as_integral());
if(!pending_events_.empty()) {
auto erase_iterator = pending_events_.begin();
int duration_remaining = integer_duration;
while(erase_iterator != pending_events_.end()) {
erase_iterator->delay -= duration_remaining;
if(erase_iterator->delay <= 0) {
duration_remaining = -erase_iterator->delay;
erase_iterator->apply(public_state_);
++erase_iterator;
} else {
break;
}
}
if(erase_iterator != pending_events_.begin()) {
pending_events_.erase(pending_events_.begin(), erase_iterator);
}
}
// integer_duration = int(duration.as_integral());
// if(!pending_events_.empty()) {
// auto erase_iterator = pending_events_.begin();
// int duration_remaining = integer_duration;
// while(erase_iterator != pending_events_.end()) {
// erase_iterator->delay -= duration_remaining;
// if(erase_iterator->delay <= 0) {
// duration_remaining = -erase_iterator->delay;
// erase_iterator->apply(public_state_);
// ++erase_iterator;
// } else {
// break;
// }
// }
// if(erase_iterator != pending_events_.begin()) {
// pending_events_.erase(pending_events_.begin(), erase_iterator);
// }
// }
}
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.
// If any events are pending, give the first of those the chance to be next.
if(!pending_events_.empty()) {
event_time = std::min(event_time, x_ + pending_events_.front().delay);
const auto next_deferred_item = deferrer_.time_until_next_action();
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.

View File

@ -122,7 +122,7 @@ class Video {
private:
void advance(HalfCycles duration);
DeferredQueue<HalfCycles> deferrer_;
DeferredQueuePerformer<HalfCycles> deferrer_;
Outputs::CRT::CRT crt_;
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) {
// Apply immediately if there's no delay (or a negative delay).
if(delay <= 0) {
@ -294,7 +294,7 @@ class Video {
} else {
pending_events_.emplace_back(type, delay);
}
}
}*/
friend class ::VideoTester;
};