mirror of
https://github.com/TomHarte/CLK.git
synced 2026-04-26 19:17:52 +00:00
Overtly link line and sprite buffers.
This commit is contained in:
@@ -56,7 +56,6 @@ Base<personality>::Base() :
|
|||||||
fetch_line_buffer_ = line_buffers_.begin();
|
fetch_line_buffer_ = line_buffers_.begin();
|
||||||
draw_line_buffer_ = line_buffers_.begin();
|
draw_line_buffer_ = line_buffers_.begin();
|
||||||
fetch_sprite_buffer_ = sprite_buffers_.begin();
|
fetch_sprite_buffer_ = sprite_buffers_.begin();
|
||||||
draw_sprite_buffer_ = sprite_buffers_.begin();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <Personality personality>
|
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
|
// Evaluation of visibility of sprite 0 is always the first step in
|
||||||
// populating a sprite buffer; so use it to uncork a new one.
|
// populating a sprite buffer; so use it to uncork a new one.
|
||||||
if(!sprite_number) {
|
if(!sprite_number) {
|
||||||
fetch_line_buffer_->fetched_sprites = true;
|
|
||||||
|
|
||||||
advance(fetch_sprite_buffer_);
|
advance(fetch_sprite_buffer_);
|
||||||
|
fetched_sprites_ = &*fetch_sprite_buffer_;
|
||||||
fetch_sprite_buffer_->reset_sprite_collection();
|
fetch_sprite_buffer_->reset_sprite_collection();
|
||||||
fetch_sprite_buffer_->sprite_terminator = mode_timing_.sprite_terminator(fetch_line_buffer_->screen_mode);
|
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_);
|
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
|
// Establish the current screen output mode, which will be captured as a
|
||||||
// line mode momentarily.
|
// 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_->next_border_column = Timing<personality>::CyclesPerLine;
|
||||||
this->fetch_line_buffer_->pixel_count = 256;
|
this->fetch_line_buffer_->pixel_count = 256;
|
||||||
this->fetch_line_buffer_->screen_mode = this->screen_mode_;
|
this->fetch_line_buffer_->screen_mode = this->screen_mode_;
|
||||||
this->fetch_line_buffer_->fetched_sprites = false;
|
|
||||||
this->mode_timing_.maximum_visible_sprites = 4;
|
this->mode_timing_.maximum_visible_sprites = 4;
|
||||||
switch(this->screen_mode_) {
|
switch(this->screen_mode_) {
|
||||||
case ScreenMode::Text:
|
case ScreenMode::Text:
|
||||||
@@ -606,12 +610,6 @@ void TMS9918<personality>::run_for(const HalfCycles cycles) {
|
|||||||
// -------------
|
// -------------
|
||||||
this->output_pointer_.column = end_column;
|
this->output_pointer_.column = end_column;
|
||||||
if(end_column == Timing<personality>::CyclesPerLine) {
|
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.
|
// Advance line buffer.
|
||||||
this->advance(this->draw_line_buffer_);
|
this->advance(this->draw_line_buffer_);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -217,13 +217,19 @@ template <Personality personality> struct Base: public Storage<personality> {
|
|||||||
using SpriteBufferArray = std::array<SpriteBuffer, 313>;
|
using SpriteBufferArray = std::array<SpriteBuffer, 313>;
|
||||||
SpriteBufferArray sprite_buffers_;
|
SpriteBufferArray sprite_buffers_;
|
||||||
SpriteBufferArray::iterator fetch_sprite_buffer_;
|
SpriteBufferArray::iterator fetch_sprite_buffer_;
|
||||||
SpriteBufferArray::iterator draw_sprite_buffer_;
|
SpriteBuffer *fetched_sprites_ = nullptr;
|
||||||
void advance(SpriteBufferArray::iterator &iterator) {
|
void advance(SpriteBufferArray::iterator &iterator) {
|
||||||
++iterator;
|
++iterator;
|
||||||
if(iterator == sprite_buffers_.end()) {
|
if(iterator == sprite_buffers_.end()) {
|
||||||
iterator = sprite_buffers_.begin();
|
iterator = sprite_buffers_.begin();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
void regress(SpriteBufferArray::iterator &iterator) {
|
||||||
|
if(iterator == sprite_buffers_.begin()) {
|
||||||
|
iterator = sprite_buffers_.end();
|
||||||
|
}
|
||||||
|
--iterator;
|
||||||
|
}
|
||||||
|
|
||||||
AddressT tile_offset_ = 0;
|
AddressT tile_offset_ = 0;
|
||||||
uint8_t name_[4]{};
|
uint8_t name_[4]{};
|
||||||
|
|||||||
@@ -14,7 +14,11 @@
|
|||||||
template <Personality personality>
|
template <Personality personality>
|
||||||
template <SpriteMode mode, bool double_width>
|
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) {
|
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) {
|
if(!buffer.active_sprite_slot) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,54 @@
|
|||||||
namespace TI::TMS {
|
namespace TI::TMS {
|
||||||
|
|
||||||
// Temporary buffers collect a representation of each line prior to pixel serialisation.
|
// 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 {
|
struct LineBuffer {
|
||||||
LineBuffer() {}
|
LineBuffer() {}
|
||||||
|
|
||||||
@@ -22,7 +70,7 @@ struct LineBuffer {
|
|||||||
FetchMode fetch_mode = FetchMode::Text;
|
FetchMode fetch_mode = FetchMode::Text;
|
||||||
ScreenMode screen_mode = ScreenMode::Text;
|
ScreenMode screen_mode = ScreenMode::Text;
|
||||||
VerticalState vertical_state = VerticalState::Blank;
|
VerticalState vertical_state = VerticalState::Blank;
|
||||||
bool fetched_sprites = false;
|
SpriteBuffer *sprites = nullptr;
|
||||||
|
|
||||||
// Holds the horizontal scroll position to apply to this line;
|
// Holds the horizontal scroll position to apply to this line;
|
||||||
// of those VDPs currently implemented, affects the Master System only.
|
// of those VDPs currently implemented, affects the Master System only.
|
||||||
@@ -75,53 +123,6 @@ struct LineBuffer {
|
|||||||
int pixel_count = 256;
|
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 {
|
struct LineBufferPointer {
|
||||||
int row = 0, column = 0;
|
int row = 0, column = 0;
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user