diff --git a/Machines/Atari2600/TIA.cpp b/Machines/Atari2600/TIA.cpp index 20c88a144..23cc6f698 100644 --- a/Machines/Atari2600/TIA.cpp +++ b/Machines/Atari2600/TIA.cpp @@ -643,6 +643,92 @@ int TIA::perform_border_motion(Object &object, int start, int end) return steps_taken; } +template void TIA::draw_object(T &target, Object &object, int start, int end) +{ + int first_pixel = first_pixel_cycle - 4 + (horizontal_blank_extend_ ? 8 : 0); + + // movement works across the entire screen, so do work that falls outside of the pixel area + if(start < first_pixel) + { + target.skip_pixels(perform_border_motion(object, start, std::max(end, first_pixel))); + } + + // don't continue to do any drawing if this window ends too early + if(end < first_pixel) return; + if(start < first_pixel) start = first_pixel; + if(start >= end) return; + + // perform the visible part of the line, if any + if(start < 224) + { + draw_object_visible(target, object, start - first_pixel_cycle + 4, std::min(end - first_pixel_cycle + 4, 160)); + } + + // move further if required + if(object.is_moving && end >= 224 && object.motion_time < end) + { + perform_motion_step(object); + target.skip_pixels(1); + } +} + +template void TIA::draw_object_visible(T &target, Object &object, int start, int end) +{ + // perform a miniature event loop on (i) triggering draws; (ii) drawing; and (iii) motion + int next_motion_time = object.motion_time - first_pixel_cycle + 4; + while(start < end) + { + int next_event_time = end; + + // is the next event a movement tick? + if(object.is_moving && next_motion_time < next_event_time) + { + next_event_time = next_motion_time; + } + + // is the next event a graphics trigger? + int next_copy = 160; + if(target.copy_flags) + { + if(object.position < 16 && target.copy_flags&1) + { + next_copy = 16; + } else if(object.position < 32 && target.copy_flags&2) + { + next_copy = 32; + } else if(object.position < 64 && target.copy_flags&4) + { + next_copy = 64; + } + } + + int next_copy_time = start + next_copy - object.position; + if(next_copy_time < next_event_time) next_event_time = next_copy_time; + + // the decision is to progress by length + const int length = next_event_time - start; + + target.draw_pixels(length); + + // the next interesting event is after next_event_time cycles, so progress + object.position = (object.position + length) % 160; + start = next_event_time; + + // if the event is a motion tick, apply + if(object.is_moving && start == next_motion_time) + { + perform_motion_step(object); + next_motion_time += 4; + } + + // if it's a draw trigger, trigger a draw + if(start == next_copy_time) + { + target.reset_pixels(); + } + } +} + #pragma mark - Player output void TIA::draw_player_visible(Player &player, Object &object, CollisionType collision_identity, int start, int end) diff --git a/Machines/Atari2600/TIA.hpp b/Machines/Atari2600/TIA.hpp index fc59a98b0..9b183ee28 100644 --- a/Machines/Atari2600/TIA.hpp +++ b/Machines/Atari2600/TIA.hpp @@ -178,14 +178,7 @@ class TIA { // indicates whether this object is currently undergoing motion bool is_moving; - // receives a list of drawing events; 20 is a deliberately over-specified quantity - struct DrawingEvent { - int time; - int copy; - } drawing_events[20]; - int draw_event_read_pointer, draw_event_write_pointer; - - Object() : draw_event_read_pointer(0), draw_event_write_pointer(0), is_moving(false) {}; + Object() : is_moving(false) {}; } object_[5]; enum class MotionIndex : uint8_t { Ball, @@ -200,6 +193,9 @@ class TIA { inline void perform_motion_step(Object &object); // drawing methods and state + template void draw_object(T &, Object &, int start, int end); + template void draw_object_visible(T &, Object &, int start, int end); + inline void output_for_cycles(int number_of_cycles); inline void output_line();