1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-07-10 12:29:01 +00:00

Made an attempt to introduce an intermediate buffer that ends up with a bit mask of all graphical components present on it, and to use that to infer collision flags and colours, based on playfield priority and colour palette. Immediately yielding: a blank screen. Good work!

This commit is contained in:
Thomas Harte 2017-02-06 18:29:00 -05:00
parent fcf8cafb5d
commit 66bcdd36f3
2 changed files with 174 additions and 47 deletions

View File

@ -11,6 +11,7 @@
using namespace Atari2600; using namespace Atari2600;
namespace { namespace {
const int cycles_per_line = 228; const int cycles_per_line = 228;
const int first_pixel_cycle = 68;
const int sync_flag = 0x1; const int sync_flag = 0x1;
const int blank_flag = 0x2; const int blank_flag = 0x2;
@ -20,7 +21,7 @@ namespace {
TIA::TIA() : TIA::TIA() :
horizontal_counter_(0), horizontal_counter_(0),
pixel_target_(nullptr), pixels_start_location_(0),
output_mode_(0), output_mode_(0),
background_{0, 0}, background_{0, 0},
background_half_mask_(0) background_half_mask_(0)
@ -36,6 +37,65 @@ TIA::TIA() :
((c & 0x10) >> 1) | ((c & 0x20) >> 3) | ((c & 0x40) >> 5) | ((c & 0x80) >> 7) ((c & 0x10) >> 1) | ((c & 0x20) >> 3) | ((c & 0x40) >> 5) | ((c & 0x80) >> 7)
); );
} }
for(int c = 0; c < 64; c++)
{
collision_flags_by_buffer_vaules_[c] = 0; // TODO
}
for(int c = 0; c < 64; c++)
{
bool has_playfield = c & (int)(CollisionType::Playfield);
bool has_ball = c & (int)(CollisionType::Ball);
bool has_player0 = c & (int)(CollisionType::Player0);
bool has_player1 = c & (int)(CollisionType::Player1);
bool has_missile0 = c & (int)(CollisionType::Missile0);
bool has_missile1 = c & (int)(CollisionType::Missile1);
colour_mask_by_mode_collision_flags_[(int)ColourMode::Standard][c] =
colour_mask_by_mode_collision_flags_[(int)ColourMode::ScoreLeft][c] =
colour_mask_by_mode_collision_flags_[(int)ColourMode::ScoreRight][c] =
colour_mask_by_mode_collision_flags_[(int)ColourMode::OnTop][c] = (uint8_t)ColourIndex::Background;
if(has_playfield || has_ball)
{
colour_mask_by_mode_collision_flags_[(int)ColourMode::Standard][c] = (uint8_t)ColourIndex::PlayfieldBall;
}
if(has_ball)
{
colour_mask_by_mode_collision_flags_[(int)ColourMode::ScoreLeft][c] =
colour_mask_by_mode_collision_flags_[(int)ColourMode::ScoreRight][c] = (uint8_t)ColourIndex::PlayfieldBall;
}
if(has_player1 || has_missile1)
{
colour_mask_by_mode_collision_flags_[(int)ColourMode::Standard][c] =
colour_mask_by_mode_collision_flags_[(int)ColourMode::ScoreLeft][c] =
colour_mask_by_mode_collision_flags_[(int)ColourMode::ScoreRight][c] =
colour_mask_by_mode_collision_flags_[(int)ColourMode::OnTop][c] = (uint8_t)ColourIndex::PlayerMissile1;
}
if(has_playfield)
{
colour_mask_by_mode_collision_flags_[(int)ColourMode::ScoreRight][c] = (uint8_t)ColourIndex::PlayerMissile1;
}
if(has_player0 || has_missile0)
{
colour_mask_by_mode_collision_flags_[(int)ColourMode::Standard][c] =
colour_mask_by_mode_collision_flags_[(int)ColourMode::ScoreLeft][c] =
colour_mask_by_mode_collision_flags_[(int)ColourMode::ScoreRight][c] =
colour_mask_by_mode_collision_flags_[(int)ColourMode::OnTop][c] = (uint8_t)ColourIndex::PlayerMissile0;
}
if(has_playfield)
{
colour_mask_by_mode_collision_flags_[(int)ColourMode::ScoreLeft][c] = (uint8_t)ColourIndex::PlayerMissile0;
}
if(has_playfield || has_ball)
{
colour_mask_by_mode_collision_flags_[(int)ColourMode::OnTop][c] = (uint8_t)ColourIndex::PlayfieldBall;
}
}
} }
void TIA::set_output_mode(Atari2600::TIA::OutputMode output_mode) void TIA::set_output_mode(Atari2600::TIA::OutputMode output_mode)
@ -131,7 +191,7 @@ int TIA::get_cycles_until_horizontal_blank(unsigned int from_offset)
void TIA::set_background_colour(uint8_t colour) void TIA::set_background_colour(uint8_t colour)
{ {
background_colour_ = colour; colour_palette_[(int)ColourIndex::Background] = colour;
} }
void TIA::set_playfield(uint16_t offset, uint8_t value) void TIA::set_playfield(uint16_t offset, uint8_t value)
@ -156,13 +216,24 @@ void TIA::set_playfield(uint16_t offset, uint8_t value)
void TIA::set_playfield_control_and_ball_size(uint8_t value) void TIA::set_playfield_control_and_ball_size(uint8_t value)
{ {
background_half_mask_ = value & 1; background_half_mask_ = value & 1;
playfield_is_above_players_ = !!(value & 4); switch(value & 6)
playfield_is_in_score_mode_ = !playfield_is_above_players_ && (value & 2); {
case 0:
playfield_priority_ = PlayfieldPriority::Standard;
break;
case 2:
playfield_priority_ = PlayfieldPriority::Score;
break;
case 4:
case 6:
playfield_priority_ = PlayfieldPriority::OnTop;
break;
}
} }
void TIA::set_playfield_ball_colour(uint8_t colour) void TIA::set_playfield_ball_colour(uint8_t colour)
{ {
playfield_ball_colour_ = colour; colour_palette_[(int)ColourIndex::PlayfieldBall] = colour;
} }
void TIA::set_player_number_and_size(int player, uint8_t value) void TIA::set_player_number_and_size(int player, uint8_t value)
@ -207,7 +278,7 @@ void TIA::set_player_delay(int player, bool delay)
void TIA::set_player_position(int player) void TIA::set_player_position(int player)
{ {
player_[player].position = ((horizontal_counter_ - 68) + 6)%160; player_[player].position = ((horizontal_counter_ - first_pixel_cycle) + 6)%160;
} }
void TIA::set_player_motion(int player, uint8_t motion) void TIA::set_player_motion(int player, uint8_t motion)
@ -217,7 +288,7 @@ void TIA::set_player_motion(int player, uint8_t motion)
void TIA::set_player_missile_colour(int player, uint8_t colour) void TIA::set_player_missile_colour(int player, uint8_t colour)
{ {
player_[player].colour = colour; colour_palette_[(int)ColourIndex::PlayerMissile0 + player] = colour;
} }
void TIA::set_missile_enable(int missile, bool enabled) void TIA::set_missile_enable(int missile, bool enabled)
@ -262,11 +333,12 @@ void TIA::clear_motion()
uint8_t TIA::get_collision_flags(int offset) uint8_t TIA::get_collision_flags(int offset)
{ {
return 0x00; return (uint8_t)((collision_flags_ >> (offset << 1)) << 6) & 0xc0;
} }
void TIA::clear_collision_flags() void TIA::clear_collision_flags()
{ {
collision_flags_ = 0;
} }
void TIA::output_for_cycles(int number_of_cycles) void TIA::output_for_cycles(int number_of_cycles)
@ -323,36 +395,76 @@ void TIA::output_for_cycles(int number_of_cycles)
if(output_mode_ & blank_flag) if(output_mode_ & blank_flag)
{ {
if(pixel_target_) if(pixels_start_location_) output_pixels(pixels_start_location_, output_cursor);
{
crt_->output_data((unsigned int)((horizontal_counter_ - pixel_target_origin_) * 2), 2);
pixel_target_ = nullptr;
}
int duration = std::min(228, horizontal_counter_) - output_cursor; int duration = std::min(228, horizontal_counter_) - output_cursor;
crt_->output_blank((unsigned int)(duration * 2)); crt_->output_blank((unsigned int)(duration * 2));
} }
else else
{ {
if(!pixel_target_) if(!pixels_start_location_) pixels_start_location_ = output_cursor;
// accumulate an OR'dversion of the output into the collision buffer
draw_playfield(output_cursor, horizontal_counter_);
// accumulate collision flags
while(output_cursor < horizontal_counter_)
{ {
pixel_target_ = crt_->allocate_write_area((unsigned int)(228 - output_cursor)); uint8_t buffer_value = collision_buffer_[output_cursor - first_pixel_cycle];
pixel_target_origin_ = output_cursor; collision_flags_ |= collision_flags_by_buffer_vaules_[buffer_value];
output_cursor++;
} }
if(pixel_target_)
{
draw_background(pixel_target_, output_cursor, horizontal_counter_);
output_cursor = horizontal_counter_;
} else output_cursor = horizontal_counter_;
if(horizontal_counter_ == cycles_per_line) if(horizontal_counter_ == cycles_per_line)
{ {
crt_->output_data((unsigned int)((horizontal_counter_ - pixel_target_origin_) * 2), 2); output_pixels(pixels_start_location_, cycles_per_line);
pixel_target_ = nullptr;
} }
} }
horizontal_counter_ %= cycles_per_line; horizontal_counter_ %= cycles_per_line;
} }
void TIA::output_pixels(int start, int end)
{
// seek a buffer and convert to pixels
uint8_t *pixel_target = crt_->allocate_write_area((unsigned int)(end - start));
if(pixel_target)
{
if(playfield_priority_ == PlayfieldPriority::Score)
{
while(start < end && start < first_pixel_cycle + 80)
{
uint8_t buffer_value = collision_buffer_[start - first_pixel_cycle];
*pixel_target = colour_palette_[colour_mask_by_mode_collision_flags_[(int)ColourMode::ScoreLeft][buffer_value]];
start++;
pixel_target++;
}
while(start < end)
{
uint8_t buffer_value = collision_buffer_[start - first_pixel_cycle];
*pixel_target = colour_palette_[colour_mask_by_mode_collision_flags_[(int)ColourMode::ScoreRight][buffer_value]];
start++;
pixel_target++;
}
}
else
{
int table_index = (int)((playfield_priority_ == PlayfieldPriority::Standard) ? ColourMode::Standard : ColourMode::OnTop);
while(start < end)
{
uint8_t buffer_value = collision_buffer_[start - first_pixel_cycle];
*pixel_target = colour_palette_[colour_mask_by_mode_collision_flags_[table_index][buffer_value]];
start++;
pixel_target++;
}
}
}
crt_->output_data((unsigned int)((end - start) * 2), 2);
pixels_start_location_ = 0;
}
void TIA::output_line() void TIA::output_line()
{ {
switch(output_mode_) switch(output_mode_)
@ -378,18 +490,13 @@ void TIA::output_line()
#pragma mark - Background and playfield #pragma mark - Background and playfield
void TIA::draw_background(uint8_t *target, int start, int length) const void TIA::draw_playfield(int start, int end)
{ {
if(!target) return;
int position = start; int position = start;
while(length--) while(position < end)
{ {
int offset = (position - 68) >> 2; int offset = (position - first_pixel_cycle) >> 2;
target[position - pixel_target_origin_] = ((background_[(offset/20)&background_half_mask_] >> (offset%20))&1) ? playfield_ball_colour_ : background_colour_; collision_buffer_[position - first_pixel_cycle] = (background_[(offset/20)&background_half_mask_] >> (offset%20))&1;
position++; position++;
} }
} }
void TIA::draw_playfield(uint8_t *target, int start, int length) const
{
}

View File

@ -75,8 +75,10 @@ class TIA {
inline void output_for_cycles(int number_of_cycles); inline void output_for_cycles(int number_of_cycles);
inline void output_line(); inline void output_line();
inline void draw_background(uint8_t *target, int start, int length) const; inline void draw_playfield(int start, int end);
inline void draw_playfield(uint8_t *target, int start, int length) const;
int pixels_start_location_;
inline void output_pixels(int start, int end);
// the master counter; counts from 0 to 228 with all visible pixels being in the final 160 // the master counter; counts from 0 to 228 with all visible pixels being in the final 160
int horizontal_counter_; int horizontal_counter_;
@ -85,24 +87,43 @@ class TIA {
int output_mode_; int output_mode_;
// keeps track of the target pixel buffer for this line and when it was acquired, and a corresponding collision buffer // keeps track of the target pixel buffer for this line and when it was acquired, and a corresponding collision buffer
uint8_t *pixel_target_;
int pixel_target_origin_;
uint8_t collision_buffer_[160]; uint8_t collision_buffer_[160];
enum class CollisionType : uint8_t { enum class CollisionType : uint8_t {
Playfield, Playfield = (1 << 0),
Sprite1, Ball = (1 << 1),
Sprite2, Player0 = (1 << 2),
Missile1, Player1 = (1 << 3),
Missile2, Missile0 = (1 << 4),
Ball Missile1 = (1 << 5)
}; };
int collision_flags_;
int collision_flags_by_buffer_vaules_[64];
// colour mapping tables
enum class ColourMode {
Standard = 0,
ScoreLeft,
ScoreRight,
OnTop
};
uint8_t colour_mask_by_mode_collision_flags_[4][64]; // maps from [ColourMode][CollisionMark] to colour_pallete_ entry
enum class ColourIndex {
Background = 0,
PlayfieldBall,
PlayerMissile1,
PlayerMissile0
};
uint8_t colour_palette_[4];
// playfield state // playfield state
uint8_t playfield_ball_colour_;
uint8_t background_colour_;
int background_half_mask_; int background_half_mask_;
bool playfield_is_in_score_mode_; enum class PlayfieldPriority {
bool playfield_is_above_players_; Standard,
Score,
OnTop
} playfield_priority_;
uint32_t background_[2]; // contains two 20-bit bitfields representing the background state; uint32_t background_[2]; // contains two 20-bit bitfields representing the background state;
// at index 0 is the left-hand side of the playfield with bit 0 being // at index 0 is the left-hand side of the playfield with bit 0 being
// the first bit to display, bit 1 the second, etc. Index 1 contains // the first bit to display, bit 1 the second, etc. Index 1 contains
@ -117,7 +138,6 @@ class TIA {
int size; // 0 = normal, 1 = double, 2 = quad int size; // 0 = normal, 1 = double, 2 = quad
int copy_flags; // a bit field, corresponding to the first few values of NUSIZ int copy_flags; // a bit field, corresponding to the first few values of NUSIZ
uint8_t graphic; // the player graphic uint8_t graphic; // the player graphic
uint8_t colour; // the player colour
int reverse_mask; // 7 for a reflected player, 0 for normal int reverse_mask; // 7 for a reflected player, 0 for normal
uint8_t motion; // low four bits used uint8_t motion; // low four bits used
uint8_t position; // in the range [0, 160) to indicate offset from the left margin, i.e. phase difference uint8_t position; // in the range [0, 160) to indicate offset from the left margin, i.e. phase difference