1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-06-26 10:29:31 +00:00

Overtly link line and sprite buffers.

This commit is contained in:
Thomas Harte 2023-04-10 23:03:39 -04:00
parent e0a5d9f31c
commit 024b7960cb
4 changed files with 69 additions and 60 deletions

View File

@ -56,7 +56,6 @@ Base<personality>::Base() :
fetch_line_buffer_ = line_buffers_.begin();
draw_line_buffer_ = line_buffers_.begin();
fetch_sprite_buffer_ = sprite_buffers_.begin();
draw_sprite_buffer_ = sprite_buffers_.begin();
}
template <Personality personality>
@ -131,9 +130,8 @@ void Base<personality>::posit_sprite(int sprite_number, int sprite_position, uin
// Evaluation of visibility of sprite 0 is always the first step in
// populating a sprite buffer; so use it to uncork a new one.
if(!sprite_number) {
fetch_line_buffer_->fetched_sprites = true;
advance(fetch_sprite_buffer_);
fetched_sprites_ = &*fetch_sprite_buffer_;
fetch_sprite_buffer_->reset_sprite_collection();
fetch_sprite_buffer_->sprite_terminator = mode_timing_.sprite_terminator(fetch_line_buffer_->screen_mode);
@ -349,6 +347,13 @@ void TMS9918<personality>::run_for(const HalfCycles cycles) {
}
this->advance(this->fetch_line_buffer_);
if(this->fetched_sprites_->active_sprite_slot) {
this->fetch_line_buffer_->sprites = this->fetched_sprites_;
this->fetched_sprites_ = nullptr;
} else {
this->fetch_line_buffer_->sprites = nullptr;
this->regress(this->fetch_sprite_buffer_);
}
// Establish the current screen output mode, which will be captured as a
// line mode momentarily.
@ -367,7 +372,6 @@ void TMS9918<personality>::run_for(const HalfCycles cycles) {
this->fetch_line_buffer_->next_border_column = Timing<personality>::CyclesPerLine;
this->fetch_line_buffer_->pixel_count = 256;
this->fetch_line_buffer_->screen_mode = this->screen_mode_;
this->fetch_line_buffer_->fetched_sprites = false;
this->mode_timing_.maximum_visible_sprites = 4;
switch(this->screen_mode_) {
case ScreenMode::Text:
@ -606,12 +610,6 @@ void TMS9918<personality>::run_for(const HalfCycles cycles) {
// -------------
this->output_pointer_.column = end_column;
if(end_column == Timing<personality>::CyclesPerLine) {
// Clear all sprites from this sprite buffer.
this->draw_sprite_buffer_->active_sprite_slot = 0;
// Advance to the next sprite buffer only if this one was populated by this line.
if(this->draw_line_buffer_->fetched_sprites) {
this->advance(this->draw_sprite_buffer_);
}
// Advance line buffer.
this->advance(this->draw_line_buffer_);
}

View File

@ -217,13 +217,19 @@ template <Personality personality> struct Base: public Storage<personality> {
using SpriteBufferArray = std::array<SpriteBuffer, 313>;
SpriteBufferArray sprite_buffers_;
SpriteBufferArray::iterator fetch_sprite_buffer_;
SpriteBufferArray::iterator draw_sprite_buffer_;
SpriteBuffer *fetched_sprites_ = nullptr;
void advance(SpriteBufferArray::iterator &iterator) {
++iterator;
if(iterator == sprite_buffers_.end()) {
iterator = sprite_buffers_.begin();
}
}
void regress(SpriteBufferArray::iterator &iterator) {
if(iterator == sprite_buffers_.begin()) {
iterator = sprite_buffers_.end();
}
--iterator;
}
AddressT tile_offset_ = 0;
uint8_t name_[4]{};

View File

@ -14,7 +14,11 @@
template <Personality personality>
template <SpriteMode mode, bool double_width>
void Base<personality>::draw_sprites(uint8_t y, int start, int end, const std::array<uint32_t, 16> &palette, int *colour_buffer) {
auto &buffer = *draw_sprite_buffer_;
if(!draw_line_buffer_->sprites) {
return;
}
auto &buffer = *draw_line_buffer_->sprites;
if(!buffer.active_sprite_slot) {
return;
}

View File

@ -14,6 +14,54 @@
namespace TI::TMS {
// Temporary buffers collect a representation of each line prior to pixel serialisation.
struct SpriteBuffer {
// An active sprite is one that has been selected for composition onto
// _this_ line.
struct ActiveSprite {
int index = 0; // The original in-table index of this sprite.
int row = 0; // The row of the sprite that should be drawn.
int x = 0; // The sprite's x position on screen.
uint8_t image[4]; // Up to four bytes of image information.
//
// In practice:
//
// Master System mode: the four bytes of this 8x8 sprite;
// TMS and Yamaha: [0] = the left half of this sprite; [1] = the right side (if 16x16 sprites enabled); [2] = colour, early-clock bit, etc.
int shift_position = 0; // An offset representing how much of the image information has already been drawn.
// Yamaha helpers.
bool opaque() const {
return !(image[2] & 0x40);
}
/// @returns @c 0x20 if this sprite should generate collisions; @c 0x00 otherwise.
int collision_bit() const {
return ((image[2] & 0x20) | ((image[2] & 0x40) >> 1)) ^ 0x20;
}
// Yamaha and TMS helpers.
int early_clock() const {
return (image[2] & 0x80) >> 2;
}
} active_sprites[8];
int active_sprite_slot = 0; // A pointer to the slot into which a new active sprite will be deposited, if required.
bool sprites_stopped = false; // A special TMS feature is that a sentinel value can be used to prevent any further sprites
// being evaluated for display. This flag determines whether the sentinel has yet been reached.
uint8_t sprite_terminator = 0;
#ifndef NDEBUG
static constexpr bool test_is_filling = true;
#else
static constexpr bool test_is_filling = false;
#endif
bool is_filling = false;
void reset_sprite_collection();
};
struct LineBuffer {
LineBuffer() {}
@ -22,7 +70,7 @@ struct LineBuffer {
FetchMode fetch_mode = FetchMode::Text;
ScreenMode screen_mode = ScreenMode::Text;
VerticalState vertical_state = VerticalState::Blank;
bool fetched_sprites = false;
SpriteBuffer *sprites = nullptr;
// Holds the horizontal scroll position to apply to this line;
// of those VDPs currently implemented, affects the Master System only.
@ -75,53 +123,6 @@ struct LineBuffer {
int pixel_count = 256;
};
struct SpriteBuffer {
// An active sprite is one that has been selected for composition onto
// _this_ line.
struct ActiveSprite {
int index = 0; // The original in-table index of this sprite.
int row = 0; // The row of the sprite that should be drawn.
int x = 0; // The sprite's x position on screen.
uint8_t image[4]; // Up to four bytes of image information.
//
// In practice:
//
// Master System mode: the four bytes of this 8x8 sprite;
// TMS and Yamaha: [0] = the left half of this sprite; [1] = the right side (if 16x16 sprites enabled); [2] = colour, early-clock bit, etc.
int shift_position = 0; // An offset representing how much of the image information has already been drawn.
// Yamaha helpers.
bool opaque() const {
return !(image[2] & 0x40);
}
/// @returns @c 0x20 if this sprite should generate collisions; @c 0x00 otherwise.
int collision_bit() const {
return ((image[2] & 0x20) | ((image[2] & 0x40) >> 1)) ^ 0x20;
}
// Yamaha and TMS helpers.
int early_clock() const {
return (image[2] & 0x80) >> 2;
}
} active_sprites[8];
int active_sprite_slot = 0; // A pointer to the slot into which a new active sprite will be deposited, if required.
bool sprites_stopped = false; // A special TMS feature is that a sentinel value can be used to prevent any further sprites
// being evaluated for display. This flag determines whether the sentinel has yet been reached.
uint8_t sprite_terminator = 0;
#ifndef NDEBUG
static constexpr bool test_is_filling = true;
#else
static constexpr bool test_is_filling = false;
#endif
bool is_filling = false;
void reset_sprite_collection();
};
struct LineBufferPointer {
int row = 0, column = 0;
};