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)
|
void TIA::set_missile_position_to_player(int missile, bool lock)
|
||||||
{
|
{
|
||||||
assert(missile >= 0 && missile < 2);
|
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;
|
missile_[missile].locked_to_player = lock;
|
||||||
|
player_[missile].latched_pixel4_time = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TIA::set_missile_motion(int missile, uint8_t motion)
|
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_playfield(latent_start, latent_end);
|
||||||
draw_object<Player>(player_[0], (uint8_t)CollisionType::Player0, output_cursor, horizontal_counter_);
|
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<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_missile(missile_[0], player_[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_[1], player_[1], (uint8_t)CollisionType::Missile1, output_cursor, horizontal_counter_);
|
||||||
draw_object<Ball>(ball_, (uint8_t)CollisionType::Ball, output_cursor, horizontal_counter_);
|
draw_object<Ball>(ball_, (uint8_t)CollisionType::Ball, output_cursor, horizontal_counter_);
|
||||||
|
|
||||||
// convert to television signals
|
// 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 == 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 == 31 && object.copy_flags&2) object.reset_pixels(2);
|
||||||
else if(object.position == 63 && object.copy_flags&4) object.reset_pixels(3);
|
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.position = (object.position + 1) % 160;
|
||||||
object.motion_step --;
|
object.motion_step --;
|
||||||
object.motion_time += 4;
|
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
|
// the decision is to progress by length
|
||||||
const int length = next_event_time - start;
|
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;
|
// enqueue a future intention to draw pixels if spitting them out now would violate accuracy;
|
||||||
// an appropriate solution would probably be to capture the drawing request into a queue and honour them outside
|
// otherwise draw them now
|
||||||
// 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.
|
|
||||||
if(object.enqueues && next_event_time > time_now)
|
if(object.enqueues && next_event_time > time_now)
|
||||||
{
|
{
|
||||||
if(start < time_now)
|
if(start < time_now)
|
||||||
{
|
{
|
||||||
object.output_pixels(&collision_buffer_[start], time_now - start, collision_identity);
|
object.output_pixels(&collision_buffer_[start], time_now - start, collision_identity, start + first_pixel_cycle - 4);
|
||||||
object.enqueue_pixels(time_now, next_event_time);
|
object.enqueue_pixels(time_now, next_event_time, time_now + first_pixel_cycle - 4);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
object.enqueue_pixels(start, next_event_time);
|
object.enqueue_pixels(start, next_event_time, start + first_pixel_cycle - 4);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
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
|
// 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 graphic_index;
|
||||||
|
|
||||||
int pixel_position;
|
int pixel_position;
|
||||||
|
int latched_pixel4_time;
|
||||||
const bool enqueues = true;
|
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);
|
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)
|
inline void reset_pixels(int copy)
|
||||||
{
|
{
|
||||||
pixel_position = 0;
|
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);
|
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)
|
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_].start = start;
|
||||||
queue_[queue_write_pointer_].end = end;
|
queue_[queue_write_pointer_].end = end;
|
||||||
@ -206,10 +213,11 @@ class TIA {
|
|||||||
queue_[queue_write_pointer_].adder = adder;
|
queue_[queue_write_pointer_].adder = adder;
|
||||||
queue_[queue_write_pointer_].reverse_mask = reverse_mask;
|
queue_[queue_write_pointer_].reverse_mask = reverse_mask;
|
||||||
queue_write_pointer_ = (queue_write_pointer_ + 1)&3;
|
queue_write_pointer_ = (queue_write_pointer_ + 1)&3;
|
||||||
skip_pixels(end - start);
|
skip_pixels(end - start, from_horizontal_counter);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
int copy_index_;
|
||||||
struct QueuedPixels
|
struct QueuedPixels
|
||||||
{
|
{
|
||||||
int start, end;
|
int start, end;
|
||||||
@ -240,7 +248,7 @@ class TIA {
|
|||||||
int size;
|
int size;
|
||||||
const bool enqueues = false;
|
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);
|
pixel_position = std::max(0, pixel_position - count);
|
||||||
}
|
}
|
||||||
@ -250,7 +258,7 @@ class TIA {
|
|||||||
pixel_position = size;
|
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;
|
int output_cursor = 0;
|
||||||
while(pixel_position && output_cursor < count)
|
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 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) {}
|
HorizontalRun() : pixel_position(0), size(1) {}
|
||||||
};
|
};
|
||||||
@ -274,16 +282,16 @@ class TIA {
|
|||||||
bool locked_to_player;
|
bool locked_to_player;
|
||||||
int copy_flags;
|
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(!pixel_position) return;
|
||||||
if(enabled && !locked_to_player)
|
if(enabled && !locked_to_player)
|
||||||
{
|
{
|
||||||
HorizontalRun::output_pixels(target, count, collision_identity);
|
HorizontalRun::output_pixels(target, count, collision_identity, from_horizontal_counter);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
skip_pixels(count);
|
skip_pixels(count, from_horizontal_counter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -296,16 +304,16 @@ class TIA {
|
|||||||
int enabled_index;
|
int enabled_index;
|
||||||
const int copy_flags = 0;
|
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(!pixel_position) return;
|
||||||
if(enabled[enabled_index])
|
if(enabled[enabled_index])
|
||||||
{
|
{
|
||||||
HorizontalRun::output_pixels(target, count, collision_identity);
|
HorizontalRun::output_pixels(target, count, collision_identity, from_horizontal_counter);
|
||||||
}
|
}
|
||||||
else
|
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);
|
template<class T> void perform_motion_step(T &object);
|
||||||
|
|
||||||
// drawing methods and state
|
// 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(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);
|
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);
|
inline void draw_playfield(int start, int end);
|
||||||
|
Loading…
Reference in New Issue
Block a user