mirror of
https://github.com/TomHarte/CLK.git
synced 2024-12-25 03:32:01 +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:
parent
440467ea3e
commit
e6a84fd26b
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user