1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-10-01 13:58:20 +00:00

Attempted a hardware-correct implementation of missile-to-player latching. This completes the last of the **knowing** inaccuracies. The rest are as-of-yet unwitting.

This commit is contained in:
Thomas Harte 2017-02-26 15:12:31 -05:00
parent 440467ea3e
commit e6a84fd26b
2 changed files with 50 additions and 28 deletions

View File

@ -354,10 +354,8 @@ void TIA::set_missile_position(int missile)
void TIA::set_missile_position_to_player(int missile, bool lock)
{
assert(missile >= 0 && missile < 2);
// TODO: implement this correctly; should be triggered by player counter hitting the appropriate point, and
// use additional storage position for enabled
if(missile_[missile].locked_to_player && !lock) missile_[missile].position = player_[missile].position + 1 + 16/player_[missile].adder;
missile_[missile].locked_to_player = lock;
player_[missile].latched_pixel4_time = -1;
}
void TIA::set_missile_motion(int missile, uint8_t motion)
@ -450,8 +448,8 @@ void TIA::output_for_cycles(int number_of_cycles)
draw_playfield(latent_start, latent_end);
draw_object<Player>(player_[0], (uint8_t)CollisionType::Player0, output_cursor, horizontal_counter_);
draw_object<Player>(player_[1], (uint8_t)CollisionType::Player1, output_cursor, horizontal_counter_);
draw_object<Missile>(missile_[0], (uint8_t)CollisionType::Missile0, output_cursor, horizontal_counter_);
draw_object<Missile>(missile_[1], (uint8_t)CollisionType::Missile1, output_cursor, horizontal_counter_);
draw_missile(missile_[0], player_[0], (uint8_t)CollisionType::Missile0, output_cursor, horizontal_counter_);
draw_missile(missile_[1], player_[1], (uint8_t)CollisionType::Missile1, output_cursor, horizontal_counter_);
draw_object<Ball>(ball_, (uint8_t)CollisionType::Ball, output_cursor, horizontal_counter_);
// convert to television signals
@ -642,7 +640,7 @@ template<class T> void TIA::perform_motion_step(T &object)
else if(object.position == 15 && object.copy_flags&1) object.reset_pixels(1);
else if(object.position == 31 && object.copy_flags&2) object.reset_pixels(2);
else if(object.position == 63 && object.copy_flags&4) object.reset_pixels(3);
else object.skip_pixels(1);
else object.skip_pixels(1, object.motion_time);
object.position = (object.position + 1) % 160;
object.motion_step --;
object.motion_time += 4;
@ -725,25 +723,23 @@ template<class T> void TIA::draw_object_visible(T &object, const uint8_t collisi
// the decision is to progress by length
const int length = next_event_time - start;
// TODO: the problem with this is that it uses the enabled/pixel state of each object four cycles early;
// an appropriate solution would probably be to capture the drawing request into a queue and honour them outside
// this loop, clipped to the real output parameters. Assuming all state consumed by draw_pixels is captured,
// and mutated now then also queueing resets and skips shouldn't be necessary.
// enqueue a future intention to draw pixels if spitting them out now would violate accuracy;
// otherwise draw them now
if(object.enqueues && next_event_time > time_now)
{
if(start < time_now)
{
object.output_pixels(&collision_buffer_[start], time_now - start, collision_identity);
object.enqueue_pixels(time_now, next_event_time);
object.output_pixels(&collision_buffer_[start], time_now - start, collision_identity, start + first_pixel_cycle - 4);
object.enqueue_pixels(time_now, next_event_time, time_now + first_pixel_cycle - 4);
}
else
{
object.enqueue_pixels(start, next_event_time);
object.enqueue_pixels(start, next_event_time, start + first_pixel_cycle - 4);
}
}
else
{
object.output_pixels(&collision_buffer_[start], length, collision_identity);
object.output_pixels(&collision_buffer_[start], length, collision_identity, start + first_pixel_cycle - 4);
}
// the next interesting event is after next_event_time cycles, so progress
@ -763,3 +759,20 @@ template<class T> void TIA::draw_object_visible(T &object, const uint8_t collisi
}
}
}
#pragma mark - Missile drawing
void TIA::draw_missile(Missile &missile, Player &player, const uint8_t collision_identity, int start, int end)
{
if(!missile.locked_to_player || player.latched_pixel4_time < 0)
{
draw_object<Missile>(missile, collision_identity, start, end);
}
else
{
draw_object<Missile>(missile, collision_identity, start, player.latched_pixel4_time);
missile.position = 0;
draw_object<Missile>(missile, collision_identity, player.latched_pixel4_time, end);
player.latched_pixel4_time = -1;
}
}

View File

@ -159,22 +159,29 @@ class TIA {
int graphic_index;
int pixel_position;
int latched_pixel4_time;
const bool enqueues = true;
inline void skip_pixels(const int count)
inline void skip_pixels(const int count, int from_horizontal_counter)
{
int old_pixel_position = pixel_position;
pixel_position = std::min(32, pixel_position + count * adder);
if(!copy_index_ && old_pixel_position < 16 && pixel_position >= 16)
{
latched_pixel4_time = from_horizontal_counter + (16 - old_pixel_position) / adder;
}
}
inline void reset_pixels(int copy)
{
pixel_position = 0;
copy_index_ = copy;
}
inline void output_pixels(uint8_t *const target, const int count, const uint8_t collision_identity)
inline void output_pixels(uint8_t *const target, const int count, const uint8_t collision_identity, int from_horizontal_counter)
{
output_pixels(target, count, collision_identity, pixel_position, adder, reverse_mask);
skip_pixels(count);
skip_pixels(count, from_horizontal_counter);
}
void dequeue_pixels(uint8_t *const target, const uint8_t collision_identity, const int time_now)
@ -198,7 +205,7 @@ class TIA {
}
}
void enqueue_pixels(const int start, const int end)
void enqueue_pixels(const int start, const int end, int from_horizontal_counter)
{
queue_[queue_write_pointer_].start = start;
queue_[queue_write_pointer_].end = end;
@ -206,10 +213,11 @@ class TIA {
queue_[queue_write_pointer_].adder = adder;
queue_[queue_write_pointer_].reverse_mask = reverse_mask;
queue_write_pointer_ = (queue_write_pointer_ + 1)&3;
skip_pixels(end - start);
skip_pixels(end - start, from_horizontal_counter);
}
private:
int copy_index_;
struct QueuedPixels
{
int start, end;
@ -240,7 +248,7 @@ class TIA {
int size;
const bool enqueues = false;
inline void skip_pixels(const int count)
inline void skip_pixels(const int count, int from_horizontal_counter)
{
pixel_position = std::max(0, pixel_position - count);
}
@ -250,7 +258,7 @@ class TIA {
pixel_position = size;
}
inline void output_pixels(uint8_t *const target, const int count, const uint8_t collision_identity)
inline void output_pixels(uint8_t *const target, const int count, const uint8_t collision_identity, int from_horizontal_counter)
{
int output_cursor = 0;
while(pixel_position && output_cursor < count)
@ -262,7 +270,7 @@ class TIA {
}
void dequeue_pixels(uint8_t *const target, const uint8_t collision_identity, const int time_now) {}
void enqueue_pixels(const int start, const int end) {}
void enqueue_pixels(const int start, const int end, int from_horizontal_counter) {}
HorizontalRun() : pixel_position(0), size(1) {}
};
@ -274,16 +282,16 @@ class TIA {
bool locked_to_player;
int copy_flags;
inline void output_pixels(uint8_t *const target, const int count, const uint8_t collision_identity)
inline void output_pixels(uint8_t *const target, const int count, const uint8_t collision_identity, int from_horizontal_counter)
{
if(!pixel_position) return;
if(enabled && !locked_to_player)
{
HorizontalRun::output_pixels(target, count, collision_identity);
HorizontalRun::output_pixels(target, count, collision_identity, from_horizontal_counter);
}
else
{
skip_pixels(count);
skip_pixels(count, from_horizontal_counter);
}
}
@ -296,16 +304,16 @@ class TIA {
int enabled_index;
const int copy_flags = 0;
inline void output_pixels(uint8_t *const target, const int count, const uint8_t collision_identity)
inline void output_pixels(uint8_t *const target, const int count, const uint8_t collision_identity, int from_horizontal_counter)
{
if(!pixel_position) return;
if(enabled[enabled_index])
{
HorizontalRun::output_pixels(target, count, collision_identity);
HorizontalRun::output_pixels(target, count, collision_identity, from_horizontal_counter);
}
else
{
skip_pixels(count);
skip_pixels(count, from_horizontal_counter);
}
}
@ -318,6 +326,7 @@ class TIA {
template<class T> void perform_motion_step(T &object);
// drawing methods and state
void draw_missile(Missile &, Player &, const uint8_t collision_identity, int start, int end);
template<class T> void draw_object(T &, const uint8_t collision_identity, int start, int end);
template<class T> void draw_object_visible(T &, const uint8_t collision_identity, int start, int end, int time_now);
inline void draw_playfield(int start, int end);