1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-12-23 20:29:42 +00:00

Attempt to make outer loop sole owner of line/sprite buffer selection.

This commit is contained in:
Thomas Harte 2023-03-30 00:20:03 -04:00
parent de3cd9c286
commit 931d2373a4
5 changed files with 200 additions and 177 deletions

View File

@ -46,11 +46,17 @@ Base<personality>::Base() :
// i.e. the fetch pointer is currently _ahead_ of the output pointer.
//
// Start at a random position.
output_pointer_.row = rand() % 262;
output_pointer_.column = rand() % (Timing<personality>::CyclesPerLine - output_lag);
output_pointer_.row = output_pointer_.column = 0;
// output_pointer_.row = rand() % 262;
// output_pointer_.column = rand() % (Timing<personality>::CyclesPerLine - output_lag);
fetch_pointer_ = output_pointer_;
fetch_pointer_.column += output_lag;
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>
@ -111,7 +117,7 @@ Outputs::Display::DisplayType TMS9918<personality>::get_display_type() const {
return this->crt_.get_display_type();
}
void LineBuffer::reset_sprite_collection() {
void SpriteBuffer::reset_sprite_collection() {
sprites_stopped = false;
active_sprite_slot = 0;
@ -121,33 +127,37 @@ void LineBuffer::reset_sprite_collection() {
}
template <Personality personality>
void Base<personality>::posit_sprite(LineBuffer &buffer, int sprite_number, int sprite_position, int screen_row) {
void Base<personality>::posit_sprite(int sprite_number, int sprite_position, uint8_t screen_row) {
// 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) {
advance(fetch_sprite_buffer_);
fetch_sprite_buffer_->reset_sprite_collection();
}
if(!(status_ & StatusSpriteOverflow)) {
status_ = uint8_t((status_ & ~0x1f) | (sprite_number & 0x1f));
}
if(buffer.sprites_stopped) return;
if(fetch_sprite_buffer_->sprites_stopped) return;
// A sprite Y of 208 means "don't scan the list any further".
if(mode_timing_.allow_sprite_terminator && sprite_position == mode_timing_.sprite_terminator(buffer.screen_mode)) {
buffer.sprites_stopped = true;
if(mode_timing_.allow_sprite_terminator && sprite_position == fetch_sprite_buffer_->sprite_terminator) {
fetch_sprite_buffer_->sprites_stopped = true;
return;
}
// TODO: the following isn't correct when fetching on the final line before pixels on the Yamaha
// VDPs when vertical offset is non-zero owing to the placement of the modulo. Will reconsider after
// dealing with total frame timing, which might affect the LineBuffer pipeline.
const int sprite_row = (((screen_row + 1) % mode_timing_.total_lines) - ((sprite_position + 1) & 255)) & 255;
const auto sprite_row = uint8_t(screen_row - sprite_position);
if(sprite_row < 0 || sprite_row >= sprite_height_) return;
if(buffer.active_sprite_slot == mode_timing_.maximum_visible_sprites) {
if(fetch_sprite_buffer_->active_sprite_slot == mode_timing_.maximum_visible_sprites) {
status_ |= StatusSpriteOverflow;
return;
}
LineBuffer::ActiveSprite &sprite = buffer.active_sprites[buffer.active_sprite_slot];
auto &sprite = fetch_sprite_buffer_->active_sprites[fetch_sprite_buffer_->active_sprite_slot];
sprite.index = sprite_number;
sprite.row = sprite_row >> (sprites_magnified_ ? 1 : 0);
++buffer.active_sprite_slot;
++fetch_sprite_buffer_->active_sprite_slot;
}
template <Personality personality>
@ -181,7 +191,6 @@ void TMS9918<personality>::run_for(const HalfCycles cycles) {
fetch_cycles_pool
);
const int end_column = this->fetch_pointer_.column + fetch_cycles;
LineBuffer &line_buffer = this->line_buffers_[this->fetch_pointer_.row];
// ... and to any pending Yamaha commands.
if constexpr (is_yamaha_vdp(personality)) {
@ -212,7 +221,7 @@ void TMS9918<personality>::run_for(const HalfCycles cycles) {
this->mode_timing_.end_of_frame_interrupt_position.row = this->mode_timing_.pixel_lines + 1;
}
}
line_buffer.latched_horizontal_scroll = Storage<personality>::horizontal_scroll_;
this->fetch_line_buffer_->latched_horizontal_scroll = Storage<personality>::horizontal_scroll_;
}
}
@ -227,14 +236,10 @@ void TMS9918<personality>::run_for(const HalfCycles cycles) {
if(first_window == final_window) break; \
if(final_window != clock_rate<personality, clock>()) { \
function<true>( \
this->line_buffers_[this->fetch_pointer_.row], \
this->line_buffers_[(this->fetch_pointer_.row + 1) % this->mode_timing_.total_lines], \
(this->fetch_pointer_.row + offset) & 0xff, \
first_window, final_window); \
} else { \
function<false>( \
this->line_buffers_[this->fetch_pointer_.row], \
this->line_buffers_[(this->fetch_pointer_.row + 1) % this->mode_timing_.total_lines], \
(this->fetch_pointer_.row + offset) & 0xff, \
first_window, final_window); \
} \
@ -243,7 +248,7 @@ void TMS9918<personality>::run_for(const HalfCycles cycles) {
if constexpr (is_yamaha_vdp(personality)) {
fetch(this->template fetch_yamaha, Clock::Internal, Storage<personality>::vertical_offset_);
} else {
switch(line_buffer.fetch_mode) {
switch(this->fetch_line_buffer_->fetch_mode) {
case FetchMode::Text: fetch(this->template fetch_tms_text, Clock::TMSMemoryWindow, 0); break;
case FetchMode::Character: fetch(this->template fetch_tms_character, Clock::TMSMemoryWindow, 0); break;
case FetchMode::SMS: fetch(this->template fetch_sms, Clock::TMSMemoryWindow, 0); break;
@ -310,10 +315,6 @@ void TMS9918<personality>::run_for(const HalfCycles cycles) {
this->vertical_active_ |= !this->fetch_pointer_.row;
this->vertical_active_ &= this->fetch_pointer_.row != this->mode_timing_.pixel_lines;
// Reset sprite collection, which will be for the line after the new one.
LineBuffer &next_sprite_buffer = this->line_buffers_[(this->fetch_pointer_.row + 1) % this->mode_timing_.total_lines];
next_sprite_buffer.reset_sprite_collection();
// Yamaha: handle blinking.
if constexpr (is_yamaha_vdp(personality)) {
if(!this->fetch_pointer_.row && Storage<personality>::blink_periods_) {
@ -325,8 +326,6 @@ void TMS9918<personality>::run_for(const HalfCycles cycles) {
}
}
LineBuffer &next_line_buffer = this->line_buffers_[this->fetch_pointer_.row];
// Progress towards any delayed events.
this->minimum_access_column_ =
std::max(
@ -341,6 +340,8 @@ void TMS9918<personality>::run_for(const HalfCycles cycles) {
);
}
this->advance(this->fetch_line_buffer_);
// Establish the current screen output mode, which will be captured as a
// line mode momentarily.
this->screen_mode_ = this->template current_screen_mode<true>();
@ -354,61 +355,62 @@ void TMS9918<personality>::run_for(const HalfCycles cycles) {
}
// Based on the output mode, pick a line mode.
next_line_buffer.first_pixel_output_column = Timing<personality>::FirstPixelCycle;
next_line_buffer.next_border_column = Timing<personality>::CyclesPerLine;
next_line_buffer.pixel_count = 256;
next_line_buffer.screen_mode = this->screen_mode_;
this->fetch_line_buffer_->first_pixel_output_column = Timing<personality>::FirstPixelCycle;
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_;
// mode_timing_.sprite_terminator(buffer.screen_mode);
this->mode_timing_.maximum_visible_sprites = 4;
switch(this->screen_mode_) {
case ScreenMode::Text:
if constexpr (is_yamaha_vdp(personality)) {
next_line_buffer.fetch_mode = FetchMode::Yamaha;
this->fetch_line_buffer_->fetch_mode = FetchMode::Yamaha;
} else {
next_line_buffer.fetch_mode = FetchMode::Text;
this->fetch_line_buffer_->fetch_mode = FetchMode::Text;
}
next_line_buffer.first_pixel_output_column = Timing<personality>::FirstTextCycle;
next_line_buffer.next_border_column = Timing<personality>::LastTextCycle;
next_line_buffer.pixel_count = 240;
this->fetch_line_buffer_->first_pixel_output_column = Timing<personality>::FirstTextCycle;
this->fetch_line_buffer_->next_border_column = Timing<personality>::LastTextCycle;
this->fetch_line_buffer_->pixel_count = 240;
break;
case ScreenMode::YamahaText80:
next_line_buffer.fetch_mode = FetchMode::Yamaha;
next_line_buffer.first_pixel_output_column = Timing<personality>::FirstTextCycle;
next_line_buffer.next_border_column = Timing<personality>::LastTextCycle;
next_line_buffer.pixel_count = 480;
this->fetch_line_buffer_->fetch_mode = FetchMode::Yamaha;
this->fetch_line_buffer_->first_pixel_output_column = Timing<personality>::FirstTextCycle;
this->fetch_line_buffer_->next_border_column = Timing<personality>::LastTextCycle;
this->fetch_line_buffer_->pixel_count = 480;
break;
case ScreenMode::SMSMode4:
next_line_buffer.fetch_mode = FetchMode::SMS;
this->fetch_line_buffer_->fetch_mode = FetchMode::SMS;
this->mode_timing_.maximum_visible_sprites = 8;
break;
case ScreenMode::YamahaGraphics3:
case ScreenMode::YamahaGraphics4:
case ScreenMode::YamahaGraphics7:
next_line_buffer.fetch_mode = FetchMode::Yamaha;
this->fetch_line_buffer_->fetch_mode = FetchMode::Yamaha;
this->mode_timing_.maximum_visible_sprites = 8;
break;
case ScreenMode::YamahaGraphics5:
case ScreenMode::YamahaGraphics6:
next_line_buffer.pixel_count = 512;
next_line_buffer.fetch_mode = FetchMode::Yamaha;
this->fetch_line_buffer_->pixel_count = 512;
this->fetch_line_buffer_->fetch_mode = FetchMode::Yamaha;
this->mode_timing_.maximum_visible_sprites = 8;
break;
default:
// This covers both MultiColour and Graphics modes.
if constexpr (is_yamaha_vdp(personality)) {
next_line_buffer.fetch_mode = FetchMode::Yamaha;
this->fetch_line_buffer_->fetch_mode = FetchMode::Yamaha;
} else {
next_line_buffer.fetch_mode = FetchMode::Character;
this->fetch_line_buffer_->fetch_mode = FetchMode::Character;
}
break;
}
next_line_buffer.vertical_state =
this->fetch_line_buffer_->vertical_state =
this->screen_mode_ == ScreenMode::Blank ?
VerticalState::Blank :
this->vertical_state();
const bool is_refresh = next_line_buffer.vertical_state == VerticalState::Blank;
const bool is_refresh = this->fetch_line_buffer_->vertical_state == VerticalState::Blank;
Storage<personality>::begin_line(this->screen_mode_, is_refresh);
@ -416,9 +418,9 @@ void TMS9918<personality>::run_for(const HalfCycles cycles) {
// The Yamaha handles refresh lines via its own microprogram; other VDPs
// can fall back on the regular refresh mechanic.
if constexpr (is_yamaha_vdp(personality)) {
next_line_buffer.fetch_mode = FetchMode::Yamaha;
this->fetch_line_buffer_->fetch_mode = FetchMode::Yamaha;
} else {
next_line_buffer.fetch_mode = FetchMode::Refresh;
this->fetch_line_buffer_->fetch_mode = FetchMode::Refresh;
}
}
}
@ -463,7 +465,6 @@ void TMS9918<personality>::run_for(const HalfCycles cycles) {
output_cycles_performed += output_cycles;
const int end_column = this->output_pointer_.column + output_cycles;
LineBuffer &line_buffer = this->line_buffers_[this->output_pointer_.row];
// --------------------
@ -485,7 +486,7 @@ void TMS9918<personality>::run_for(const HalfCycles cycles) {
#define border(left, right) intersect(left, right, this->output_border(end - start, cram_value))
if(line_buffer.vertical_state != VerticalState::Pixels) {
if(this->draw_line_buffer_->vertical_state != VerticalState::Pixels) {
if(
this->output_pointer_.row >= this->mode_timing_.first_vsync_line &&
this->output_pointer_.row < this->mode_timing_.first_vsync_line + 4
@ -532,43 +533,43 @@ void TMS9918<personality>::run_for(const HalfCycles cycles) {
}
// Left border.
border(Timing<personality>::StartOfLeftBorder, line_buffer.first_pixel_output_column);
border(Timing<personality>::StartOfLeftBorder, this->draw_line_buffer_->first_pixel_output_column);
#define draw(function, clock) { \
const int relative_start = from_internal<personality, clock>(start - line_buffer.first_pixel_output_column); \
const int relative_end = from_internal<personality, clock>(end - line_buffer.first_pixel_output_column); \
if(relative_start == relative_end) break; \
#define draw(function, clock) { \
const int relative_start = from_internal<personality, clock>(start - this->draw_line_buffer_->first_pixel_output_column); \
const int relative_end = from_internal<personality, clock>(end - this->draw_line_buffer_->first_pixel_output_column); \
if(relative_start == relative_end) break; \
this->function; }
// Pixel region.
intersect(
line_buffer.first_pixel_output_column,
line_buffer.next_border_column,
this->draw_line_buffer_->first_pixel_output_column,
this->draw_line_buffer_->next_border_column,
if(!this->asked_for_write_area_) {
this->asked_for_write_area_ = true;
this->pixel_origin_ = this->pixel_target_ = reinterpret_cast<uint32_t *>(
this->crt_.begin_data(size_t(line_buffer.pixel_count))
this->crt_.begin_data(size_t(this->draw_line_buffer_->pixel_count))
);
}
if(this->pixel_target_) {
if constexpr (is_yamaha_vdp(personality)) {
draw(draw_yamaha(relative_start, relative_end), Clock::Internal);
draw(draw_yamaha(0, relative_start, relative_end), Clock::Internal); // TODO: what is the correct 'y'?
} else {
switch(line_buffer.fetch_mode) {
switch(this->draw_line_buffer_->fetch_mode) {
case FetchMode::SMS: draw(draw_sms(relative_start, relative_end, cram_value), Clock::TMSPixel); break;
case FetchMode::Character: draw(draw_tms_character(relative_start, relative_end), Clock::TMSPixel); break;
case FetchMode::Text: draw(template draw_tms_text<false>(relative_start, relative_end), Clock::TMSPixel); break;
case FetchMode::Text: draw(template draw_tms_text<false>(relative_start, relative_end), Clock::TMSPixel); break;
default: break; /* Dealt with elsewhere. */
}
}
}
if(end == line_buffer.next_border_column) {
const int length = line_buffer.next_border_column - line_buffer.first_pixel_output_column;
this->crt_.output_data(from_internal<personality, Clock::CRT>(length), size_t(line_buffer.pixel_count));
if(end == this->draw_line_buffer_->next_border_column) {
const int length = this->draw_line_buffer_->next_border_column - this->draw_line_buffer_->first_pixel_output_column;
this->crt_.output_data(from_internal<personality, Clock::CRT>(length), size_t(this->draw_line_buffer_->pixel_count));
this->pixel_origin_ = this->pixel_target_ = nullptr;
this->asked_for_write_area_ = false;
}
@ -577,8 +578,8 @@ void TMS9918<personality>::run_for(const HalfCycles cycles) {
#undef draw
// Additional right border, if called for.
if(line_buffer.next_border_column != Timing<personality>::CyclesPerLine) {
border(line_buffer.next_border_column, Timing<personality>::CyclesPerLine);
if(this->draw_line_buffer_->next_border_column != Timing<personality>::CyclesPerLine) {
border(this->draw_line_buffer_->next_border_column, Timing<personality>::CyclesPerLine);
}
}
@ -596,6 +597,10 @@ void TMS9918<personality>::run_for(const HalfCycles cycles) {
// Advance time.
// -------------
this->output_pointer_.column = end_column;
if(end_column == Timing<personality>::CyclesPerLine) {
this->advance(this->draw_line_buffer_);
this->advance(this->draw_sprite_buffer_);
}
}
output_cycles_pool -= target_output_cycles;

View File

@ -124,7 +124,7 @@ template <Personality personality> struct Base: public Storage<personality> {
bool sprites_16x16_ = false;
bool sprites_magnified_ = false;
bool generate_interrupts_ = false;
int sprite_height_ = 8;
uint8_t sprite_height_ = 8;
// Programmer-specified addresses.
//
@ -202,10 +202,32 @@ template <Personality personality> struct Base: public Storage<personality> {
bool vertical_active_ = false;
ScreenMode screen_mode_, underlying_mode_;
LineBuffer line_buffers_[313];
using LineBufferArray = std::array<LineBuffer, 313>;
LineBufferArray line_buffers_;
LineBufferArray::iterator fetch_line_buffer_;
LineBufferArray::iterator draw_line_buffer_;
void advance(LineBufferArray::iterator &iterator) {
++iterator;
if(iterator == line_buffers_.end()) {
iterator = line_buffers_.begin();
}
}
using SpriteBufferArray = std::array<SpriteBuffer, 313>;
SpriteBufferArray sprite_buffers_;
SpriteBufferArray::iterator fetch_sprite_buffer_;
SpriteBufferArray::iterator draw_sprite_buffer_;
void advance(SpriteBufferArray::iterator &iterator) {
++iterator;
if(iterator == sprite_buffers_.end()) {
iterator = sprite_buffers_.begin();
}
}
AddressT tile_offset_ = 0;
uint8_t name_[4]{};
void posit_sprite(LineBuffer &buffer, int sprite_number, int sprite_y, int screen_row);
void posit_sprite(int sprite_number, int sprite_y, uint8_t screen_row);
// There is a delay between reading into the line buffer and outputting from there to the screen. That delay
// is observeable because reading time affects availability of memory accesses and therefore time in which
@ -563,14 +585,14 @@ template <Personality personality> struct Base: public Storage<personality> {
template<bool use_end, typename Fetcher> void dispatch(Fetcher &fetcher, int start, int end);
// Various fetchers.
template<bool use_end> void fetch_tms_refresh(LineBuffer &, LineBuffer &, int y, int start, int end);
template<bool use_end> void fetch_tms_text(LineBuffer &, LineBuffer &, int y, int start, int end);
template<bool use_end> void fetch_tms_character(LineBuffer &, LineBuffer &, int y, int start, int end);
template<bool use_end> void fetch_tms_refresh(uint8_t y, int start, int end);
template<bool use_end> void fetch_tms_text(uint8_t y, int start, int end);
template<bool use_end> void fetch_tms_character(uint8_t y, int start, int end);
template<bool use_end> void fetch_yamaha(LineBuffer &, LineBuffer &, int y, int start, int end);
template<ScreenMode> void fetch_yamaha(LineBuffer &, LineBuffer &, int y, int end);
template<bool use_end> void fetch_yamaha(uint8_t y, int start, int end);
template<ScreenMode> void fetch_yamaha(uint8_t y, int end);
template<bool use_end> void fetch_sms(LineBuffer &, LineBuffer &, int y, int start, int end);
template<bool use_end> void fetch_sms(uint8_t y, int start, int end);
// A helper function to output the current border colour for
// the number of cycles supplied.
@ -585,10 +607,10 @@ template <Personality personality> struct Base: public Storage<personality> {
template <bool apply_blink> void draw_tms_text(int start, int end);
void draw_sms(int start, int end, uint32_t cram_dot);
template<ScreenMode mode> void draw_yamaha(LineBuffer &, int y, int start, int end);
void draw_yamaha(int start, int end);
template<ScreenMode mode> void draw_yamaha(uint8_t y, int start, int end);
void draw_yamaha(uint8_t y, int start, int end);
template <SpriteMode mode, bool double_width> void draw_sprites(LineBuffer &, int y, int start, int end, const std::array<uint32_t, 16> &palette, int *colour_buffer = nullptr);
template <SpriteMode mode, bool double_width> void draw_sprites(uint8_t y, int start, int end, const std::array<uint32_t, 16> &palette, int *colour_buffer = nullptr);
};
#include "Fetch.hpp"

View File

@ -13,7 +13,8 @@
template <Personality personality>
template <SpriteMode mode, bool double_width>
void Base<personality>::draw_sprites(LineBuffer &buffer, [[maybe_unused]] int 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(!buffer.active_sprite_slot) {
return;
}
@ -23,7 +24,7 @@ void Base<personality>::draw_sprites(LineBuffer &buffer, [[maybe_unused]] int y,
// If this is the start of the line clip any part of any sprites that is off to the left.
if(!start) {
for(int index = 0; index < buffer.active_sprite_slot; ++index) {
LineBuffer::ActiveSprite &sprite = buffer.active_sprites[index];
auto &sprite = buffer.active_sprites[index];
if(sprite.x < 0) sprite.shift_position -= shift_advance * sprite.x;
}
}
@ -35,7 +36,7 @@ void Base<personality>::draw_sprites(LineBuffer &buffer, [[maybe_unused]] int y,
if constexpr (mode == SpriteMode::MasterSystem) {
// Draw all sprites into the sprite buffer.
for(int index = buffer.active_sprite_slot - 1; index >= 0; --index) {
LineBuffer::ActiveSprite &sprite = buffer.active_sprites[index];
auto &sprite = buffer.active_sprites[index];
if(sprite.shift_position >= 16) {
continue;
}
@ -96,7 +97,7 @@ void Base<personality>::draw_sprites(LineBuffer &buffer, [[maybe_unused]] int y,
if constexpr (mode == SpriteMode::Mode2) {
// Determine the lowest visible sprite; exit early if that leaves no sprites visible.
for(; min_sprite < buffer.active_sprite_slot; min_sprite++) {
LineBuffer::ActiveSprite &sprite = buffer.active_sprites[min_sprite];
auto &sprite = buffer.active_sprites[min_sprite];
if(sprite.opaque()) {
break;
}
@ -109,7 +110,7 @@ void Base<personality>::draw_sprites(LineBuffer &buffer, [[maybe_unused]] int y,
// Pre-rasterise the sprites one-by-one.
if(sprites_magnified_) {
for(int index = min_sprite; index < buffer.active_sprite_slot; index++) {
LineBuffer::ActiveSprite &sprite = buffer.active_sprites[index];
auto &sprite = buffer.active_sprites[index];
for(int c = 0; c < 32; c+= 2) {
const int shift = (c >> 1) ^ 7;
const int bit = 1 & (sprite.image[shift >> 3] >> (shift & 7));
@ -122,7 +123,7 @@ void Base<personality>::draw_sprites(LineBuffer &buffer, [[maybe_unused]] int y,
}
} else {
for(int index = min_sprite; index < buffer.active_sprite_slot; index++) {
LineBuffer::ActiveSprite &sprite = buffer.active_sprites[index];
auto &sprite = buffer.active_sprites[index];
for(int c = 0; c < 16; c++) {
const int shift = c ^ 7;
const int bit = 1 & (sprite.image[shift >> 3] >> (shift & 7));
@ -136,7 +137,7 @@ void Base<personality>::draw_sprites(LineBuffer &buffer, [[maybe_unused]] int y,
// Go backwards compositing any sprites that are set as OR masks onto their parents.
for(int index = buffer.active_sprite_slot - 1; index >= min_sprite + 1; --index) {
LineBuffer::ActiveSprite &sprite = buffer.active_sprites[index];
auto &sprite = buffer.active_sprites[index];
if(sprite.opaque()) {
continue;
}
@ -144,7 +145,7 @@ void Base<personality>::draw_sprites(LineBuffer &buffer, [[maybe_unused]] int y,
// Sprite may affect all previous up to and cindlugin the next one that is opaque.
for(int previous_index = index - 1; previous_index >= min_sprite; --previous_index) {
// Determine region of overlap (if any).
LineBuffer::ActiveSprite &previous = buffer.active_sprites[previous_index];
auto &previous = buffer.active_sprites[previous_index];
const int origin = sprite.x - previous.x;
const int x1 = std::max(0, -origin);
const int x2 = std::min(pixel_width - origin, pixel_width);
@ -165,7 +166,7 @@ void Base<personality>::draw_sprites(LineBuffer &buffer, [[maybe_unused]] int y,
// Draw.
for(int index = buffer.active_sprite_slot - 1; index >= min_sprite; --index) {
LineBuffer::ActiveSprite &sprite = buffer.active_sprites[index];
auto &sprite = buffer.active_sprites[index];
const int x1 = std::max(0, start - sprite.x);
const int x2 = std::min(end - sprite.x, pixel_width);
@ -198,7 +199,7 @@ void Base<personality>::draw_sprites(LineBuffer &buffer, [[maybe_unused]] int y,
if constexpr (mode == SpriteMode::Mode1) {
for(int index = buffer.active_sprite_slot - 1; index >= min_sprite; --index) {
LineBuffer::ActiveSprite &sprite = buffer.active_sprites[index];
auto &sprite = buffer.active_sprites[index];
if(sprite.shift_position >= shifter_target) {
continue;
}
@ -246,7 +247,7 @@ void Base<personality>::draw_sprites(LineBuffer &buffer, [[maybe_unused]] int y,
template <Personality personality>
template <SpriteMode sprite_mode>
void Base<personality>::draw_tms_character(int start, int end) {
LineBuffer &line_buffer = line_buffers_[output_pointer_.row];
auto &line_buffer = *draw_line_buffer_;
// Paint the background tiles.
const int pixels_left = end - start;
@ -289,14 +290,13 @@ void Base<personality>::draw_tms_character(int start, int end) {
}
}
draw_sprites<sprite_mode, false>(line_buffer, output_pointer_.row, start, end, palette());
draw_sprites<sprite_mode, false>(0, start, end, palette()); // TODO: propagate a real 'y' into here.
}
template <Personality personality>
template <bool apply_blink>
void Base<personality>::draw_tms_text(int start, int end) {
LineBuffer &line_buffer = line_buffers_[output_pointer_.row];
auto &line_buffer = *draw_line_buffer_;
uint32_t colours[2][2] = {
{palette()[background_colour_], palette()[text_colour_]},
{0, 0}
@ -338,8 +338,8 @@ void Base<personality>::draw_tms_text(int start, int end) {
template <Personality personality>
void Base<personality>::draw_sms(int start, int end, uint32_t cram_dot) {
if constexpr (is_sega_vdp(personality)) {
LineBuffer &line_buffer = line_buffers_[output_pointer_.row];
int colour_buffer[256];
auto &line_buffer = *draw_line_buffer_;
/*
Add extra border for any pixels that fall before the fine scroll.
@ -416,7 +416,7 @@ void Base<personality>::draw_sms(int start, int end, uint32_t cram_dot) {
/*
Apply sprites (if any).
*/
draw_sprites<SpriteMode::MasterSystem, false>(line_buffer, output_pointer_.row, start, end, palette(), colour_buffer);
draw_sprites<SpriteMode::MasterSystem, false>(0, start, end, palette(), colour_buffer); // TODO provide good y, as per elsewhere.
// Map from the 32-colour buffer to real output pixels, applying the specific CRAM dot if any.
pixel_target_[start] = Storage<personality>::colour_ram_[colour_buffer[start] & 0x1f] | cram_dot;
@ -440,10 +440,11 @@ void Base<personality>::draw_sms(int start, int end, uint32_t cram_dot) {
template <Personality personality>
template <ScreenMode mode>
void Base<personality>::draw_yamaha(LineBuffer &buffer, int y, int start, int end) {
void Base<personality>::draw_yamaha(uint8_t y, int start, int end) {
const auto active_palette = palette();
const int sprite_start = start >> 2;
const int sprite_end = end >> 2;
auto &line_buffer = *draw_line_buffer_;
// Observation justifying Duff's device below: it's acceptable to paint too many pixels — to paint
// beyond `end` — provided that the overpainting is within normal bitmap bounds, because any
@ -461,8 +462,8 @@ void Base<personality>::draw_yamaha(LineBuffer &buffer, int y, int start, int en
switch(offset) {
case 0:
do {
pixel_target_[column+0] = active_palette[buffer.bitmap[start] >> 4];
case 1: pixel_target_[column+1] = active_palette[buffer.bitmap[start] & 0xf];
pixel_target_[column+0] = active_palette[line_buffer.bitmap[start] >> 4];
case 1: pixel_target_[column+1] = active_palette[line_buffer.bitmap[start] & 0xf];
++start;
column += 2;
} while(start < end);
@ -481,10 +482,10 @@ void Base<personality>::draw_yamaha(LineBuffer &buffer, int y, int start, int en
switch(offset) {
case 0:
do {
pixel_target_[column+0] = active_palette[buffer.bitmap[start] >> 6];
case 1: pixel_target_[column+1] = active_palette[(buffer.bitmap[start] >> 4) & 3];
case 2: pixel_target_[column+2] = active_palette[(buffer.bitmap[start] >> 2) & 3];
case 3: pixel_target_[column+3] = active_palette[buffer.bitmap[start] & 3];
pixel_target_[column+0] = active_palette[line_buffer.bitmap[start] >> 6];
case 1: pixel_target_[column+1] = active_palette[(line_buffer.bitmap[start] >> 4) & 3];
case 2: pixel_target_[column+2] = active_palette[(line_buffer.bitmap[start] >> 2) & 3];
case 3: pixel_target_[column+3] = active_palette[line_buffer.bitmap[start] & 3];
++start;
column += 4;
} while(start < end);
@ -498,9 +499,9 @@ void Base<personality>::draw_yamaha(LineBuffer &buffer, int y, int start, int en
while(start < end) {
pixel_target_[start] =
palette_pack(
uint8_t((buffer.bitmap[start] & 0x1c) + ((buffer.bitmap[start] & 0x1c) << 3) + ((buffer.bitmap[start] & 0x1c) >> 3)),
uint8_t((buffer.bitmap[start] & 0xe0) + ((buffer.bitmap[start] & 0xe0) >> 3) + ((buffer.bitmap[start] & 0xe0) >> 6)),
uint8_t((buffer.bitmap[start] & 0x03) + ((buffer.bitmap[start] & 0x03) << 2) + ((buffer.bitmap[start] & 0x03) << 4) + ((buffer.bitmap[start] & 0x03) << 6))
uint8_t((line_buffer.bitmap[start] & 0x1c) + ((line_buffer.bitmap[start] & 0x1c) << 3) + ((line_buffer.bitmap[start] & 0x1c) >> 3)),
uint8_t((line_buffer.bitmap[start] & 0xe0) + ((line_buffer.bitmap[start] & 0xe0) >> 3) + ((line_buffer.bitmap[start] & 0xe0) >> 6)),
uint8_t((line_buffer.bitmap[start] & 0x03) + ((line_buffer.bitmap[start] & 0x03) << 2) + ((line_buffer.bitmap[start] & 0x03) << 4) + ((line_buffer.bitmap[start] & 0x03) << 6))
);
++start;
}
@ -522,15 +523,13 @@ void Base<personality>::draw_yamaha(LineBuffer &buffer, int y, int start, int en
draw_sprites<
SpriteMode::Mode2,
mode == ScreenMode::YamahaGraphics5 || mode == ScreenMode::YamahaGraphics6
>(buffer, y, sprite_start, sprite_end, mode == ScreenMode::YamahaGraphics7 ? graphics7_sprite_palette : palette());
>(y, sprite_start, sprite_end, mode == ScreenMode::YamahaGraphics7 ? graphics7_sprite_palette : palette());
}
template <Personality personality>
void Base<personality>::draw_yamaha(int start, int end) {
LineBuffer &line_buffer = line_buffers_[output_pointer_.row];
void Base<personality>::draw_yamaha(uint8_t y, int start, int end) {
if constexpr (is_yamaha_vdp(personality)) {
switch(line_buffer.screen_mode) {
switch(draw_line_buffer_->screen_mode) {
// Modes that are the same (or close enough) to those on the TMS.
case ScreenMode::Text: draw_tms_text<false>(start >> 2, end >> 2); break;
case ScreenMode::YamahaText80: draw_tms_text<true>(start >> 1, end >> 1); break;
@ -542,7 +541,7 @@ void Base<personality>::draw_yamaha(int start, int end) {
draw_tms_character<SpriteMode::Mode2>(start >> 2, end >> 2);
break;
#define Dispatch(x) case ScreenMode::x: draw_yamaha<ScreenMode::x>(line_buffer, output_pointer_.row, start, end); break;
#define Dispatch(x) case ScreenMode::x: draw_yamaha<ScreenMode::x>(y, start, end); break;
Dispatch(YamahaGraphics4);
Dispatch(YamahaGraphics5);
Dispatch(YamahaGraphics6);

View File

@ -97,9 +97,8 @@ template <Personality personality>
struct TextFetcher {
using AddressT = typename Base<personality>::AddressT;
TextFetcher(Base<personality> *base, LineBuffer &buffer, int y) :
TextFetcher(Base<personality> *base, uint8_t y) :
base(base),
line_buffer(buffer),
row_base(base->pattern_name_address_ & bits<10>(AddressT((y >> 3) * 40))),
row_offset(base->pattern_generator_table_address_ & bits<11>(AddressT(y & 7))) {}
@ -108,11 +107,10 @@ struct TextFetcher {
}
void fetch_pattern(AddressT column, int slot = 0) {
line_buffer.characters.shapes[column] = base->ram_[row_offset + size_t(base->name_[slot] << 3)];
base->fetch_line_buffer_->characters.shapes[column] = base->ram_[row_offset + size_t(base->name_[slot] << 3)];
}
Base<personality> *const base;
LineBuffer &line_buffer;
const AddressT row_base;
const AddressT row_offset;
};
@ -121,10 +119,8 @@ template <Personality personality>
struct CharacterFetcher {
using AddressT = typename Base<personality>::AddressT;
CharacterFetcher(Base<personality> *base, LineBuffer &buffer, LineBuffer &sprite_selection_buffer, int y) :
CharacterFetcher(Base<personality> *base, uint8_t y) :
base(base),
tile_buffer(buffer),
sprite_selection_buffer(sprite_selection_buffer),
y(y),
row_base(base->pattern_name_address_ & bits<10>(AddressT((y << 2)&~31)))
{
@ -132,7 +128,8 @@ struct CharacterFetcher {
colour_base = base->colour_table_address_;
colour_name_shift = 6;
if(buffer.screen_mode == ScreenMode::Graphics || buffer.screen_mode == ScreenMode::YamahaGraphics3) {
const ScreenMode mode = base->fetch_line_buffer_->screen_mode;
if(mode == ScreenMode::Graphics || mode == ScreenMode::YamahaGraphics3) {
// If this is high resolution mode, allow the row number to affect the pattern and colour addresses.
pattern_base &= bits<13>(AddressT(((y & 0xc0) << 5)));
colour_base &= bits<13>(AddressT(((y & 0xc0) << 5)));
@ -144,7 +141,7 @@ struct CharacterFetcher {
pattern_base &= bits<11, AddressT>();
}
if(buffer.screen_mode == ScreenMode::MultiColour) {
if(mode == ScreenMode::MultiColour) {
pattern_base += AddressT((y >> 2) & 7);
} else {
pattern_base += AddressT(y & 7);
@ -156,17 +153,15 @@ struct CharacterFetcher {
}
void fetch_pattern(int column) {
tile_buffer.tiles.patterns[column][0] = base->ram_[pattern_base + AddressT(base->tile_offset_ << 3)];
base->fetch_line_buffer_->tiles.patterns[column][0] = base->ram_[pattern_base + AddressT(base->tile_offset_ << 3)];
}
void fetch_colour(int column) {
tile_buffer.tiles.patterns[column][1] = base->ram_[colour_base + AddressT((base->tile_offset_ << 3) >> colour_name_shift)];
base->fetch_line_buffer_->tiles.patterns[column][1] = base->ram_[colour_base + AddressT((base->tile_offset_ << 3) >> colour_name_shift)];
}
Base<personality> *const base;
LineBuffer &tile_buffer;
LineBuffer &sprite_selection_buffer;
const int y;
const uint8_t y;
const AddressT row_base;
AddressT pattern_base;
AddressT colour_base;
@ -204,10 +199,8 @@ class SpriteFetcher {
// AttributeAddressMask is used to enable or disable that behaviour.
static constexpr AddressT AttributeAddressMask = (mode == SpriteMode::Mode2) ? AddressT(~0x180) : AddressT(~0x000);
SpriteFetcher(Base<personality> *base, LineBuffer &buffer, LineBuffer &sprite_selection_buffer, int y) :
SpriteFetcher(Base<personality> *base, uint8_t y) :
base(base),
tile_buffer(buffer),
sprite_selection_buffer(sprite_selection_buffer),
y(y) {}
void fetch_location(int slot) {
@ -237,29 +230,31 @@ class SpriteFetcher {
void fetch_y(int sprite) {
const AddressT address = base->sprite_attribute_table_address_ & AttributeAddressMask & bits<7>(AddressT(sprite << 2));
const uint8_t sprite_y = base->ram_[address];
base->posit_sprite(sprite_selection_buffer, sprite, sprite_y, y);
base->posit_sprite(sprite, sprite_y, y);
}
private:
void fetch_xy(int slot) {
tile_buffer.active_sprites[slot].x =
auto &buffer = *base->fetch_sprite_buffer_;
buffer.active_sprites[slot].x =
base->ram_[
base->sprite_attribute_table_address_ & AttributeAddressMask & bits<7>(AddressT((tile_buffer.active_sprites[slot].index << 2) | 1))
base->sprite_attribute_table_address_ & AttributeAddressMask & bits<7>(AddressT((buffer.active_sprites[slot].index << 2) | 1))
];
}
uint8_t name(int slot) {
auto &buffer = *base->fetch_sprite_buffer_;
const AddressT address =
base->sprite_attribute_table_address_ &
AttributeAddressMask &
bits<7>(AddressT((tile_buffer.active_sprites[slot].index << 2) | 2));
bits<7>(AddressT((buffer.active_sprites[slot].index << 2) | 2));
const uint8_t name = base->ram_[address] & (base->sprites_16x16_ ? ~3 : ~0);
return name;
}
void fetch_image(int slot, uint8_t name) {
uint8_t colour = 0;
auto &sprite = tile_buffer.active_sprites[slot];
auto &sprite = base->fetch_sprite_buffer_->active_sprites[slot];
switch(mode) {
case SpriteMode::Mode1:
// Fetch colour from the attribute table, per this sprite's slot.
@ -289,9 +284,7 @@ class SpriteFetcher {
}
Base<personality> *const base;
LineBuffer &tile_buffer;
LineBuffer &sprite_selection_buffer;
const int y;
const uint8_t y;
};
template <Personality personality>
@ -302,13 +295,11 @@ struct SMSFetcher {
AddressT sub_row[2];
};
SMSFetcher(Base<personality> *base, LineBuffer &buffer, LineBuffer &sprite_selection_buffer, int y) :
SMSFetcher(Base<personality> *base, uint8_t y) :
base(base),
storage(static_cast<Storage<personality> *>(base)),
tile_buffer(buffer),
sprite_selection_buffer(sprite_selection_buffer),
y(y),
horizontal_offset((y >= 16 || !storage->horizontal_scroll_lock_) ? (tile_buffer.latched_horizontal_scroll >> 3) : 0)
horizontal_offset((y >= 16 || !storage->horizontal_scroll_lock_) ? (base->fetch_line_buffer_->latched_horizontal_scroll >> 3) : 0)
{
// Limit address bits in use if this is a SMS2 mode.
const bool is_tall_mode = base->mode_timing_.pixel_lines != 192;
@ -329,50 +320,51 @@ struct SMSFetcher {
}
void fetch_sprite(int sprite) {
tile_buffer.active_sprites[sprite].x =
auto &sprite_buffer = *base->fetch_sprite_buffer_;
sprite_buffer.active_sprites[sprite].x =
base->ram_[
storage->sprite_attribute_table_address_ & bits<7>((tile_buffer.active_sprites[sprite].index << 1) | 0)
storage->sprite_attribute_table_address_ & bits<7>((sprite_buffer.active_sprites[sprite].index << 1) | 0)
] - (storage->shift_sprites_8px_left_ ? 8 : 0);
const uint8_t name = base->ram_[
storage->sprite_attribute_table_address_ & bits<7>((tile_buffer.active_sprites[sprite].index << 1) | 1)
storage->sprite_attribute_table_address_ & bits<7>((sprite_buffer.active_sprites[sprite].index << 1) | 1)
] & (base->sprites_16x16_ ? ~1 : ~0);
const AddressT graphic_location =
storage->sprite_generator_table_address_ &
bits<13>(AddressT((name << 5) | (tile_buffer.active_sprites[sprite].row << 2)));
tile_buffer.active_sprites[sprite].image[0] = base->ram_[graphic_location];
tile_buffer.active_sprites[sprite].image[1] = base->ram_[graphic_location+1];
tile_buffer.active_sprites[sprite].image[2] = base->ram_[graphic_location+2];
tile_buffer.active_sprites[sprite].image[3] = base->ram_[graphic_location+3];
bits<13>(AddressT((name << 5) | (sprite_buffer.active_sprites[sprite].row << 2)));
sprite_buffer.active_sprites[sprite].image[0] = base->ram_[graphic_location];
sprite_buffer.active_sprites[sprite].image[1] = base->ram_[graphic_location+1];
sprite_buffer.active_sprites[sprite].image[2] = base->ram_[graphic_location+2];
sprite_buffer.active_sprites[sprite].image[3] = base->ram_[graphic_location+3];
}
void fetch_tile_name(int column) {
const RowInfo &row_info = column < 24 ? scrolled_row_info : static_row_info;
const size_t scrolled_column = (column - horizontal_offset) & 0x1f;
const size_t address = row_info.pattern_address_base + (scrolled_column << 1);
auto &line_buffer = *base->fetch_line_buffer_;
tile_buffer.tiles.flags[column] = base->ram_[address+1];
line_buffer.tiles.flags[column] = base->ram_[address+1];
base->tile_offset_ = AddressT(
(((tile_buffer.tiles.flags[column]&1) << 8) | base->ram_[address]) << 5
) + row_info.sub_row[(tile_buffer.tiles.flags[column]&4) >> 2];
(((line_buffer.tiles.flags[column]&1) << 8) | base->ram_[address]) << 5
) + row_info.sub_row[(line_buffer.tiles.flags[column]&4) >> 2];
}
void fetch_tile_pattern(int column) {
tile_buffer.tiles.patterns[column][0] = base->ram_[base->tile_offset_];
tile_buffer.tiles.patterns[column][1] = base->ram_[base->tile_offset_+1];
tile_buffer.tiles.patterns[column][2] = base->ram_[base->tile_offset_+2];
tile_buffer.tiles.patterns[column][3] = base->ram_[base->tile_offset_+3];
auto &line_buffer = *base->fetch_line_buffer_;
line_buffer.tiles.patterns[column][0] = base->ram_[base->tile_offset_];
line_buffer.tiles.patterns[column][1] = base->ram_[base->tile_offset_+1];
line_buffer.tiles.patterns[column][2] = base->ram_[base->tile_offset_+2];
line_buffer.tiles.patterns[column][3] = base->ram_[base->tile_offset_+3];
}
void posit_sprite(int sprite) {
base->posit_sprite(sprite_selection_buffer, sprite, base->ram_[storage->sprite_attribute_table_address_ & bits<8>(AddressT(sprite))], y);
base->posit_sprite(sprite, base->ram_[storage->sprite_attribute_table_address_ & bits<8>(AddressT(sprite))], y);
}
Base<personality> *const base;
const Storage<personality> *const storage;
LineBuffer &tile_buffer;
LineBuffer &sprite_selection_buffer;
const int y;
const uint8_t y;
const int horizontal_offset;
RowInfo scrolled_row_info, static_row_info;
};
@ -490,20 +482,20 @@ struct CharacterSequencer {
// MARK: - TMS fetch routines.
template <Personality personality>
template<bool use_end> void Base<personality>::fetch_tms_refresh(LineBuffer &, LineBuffer &, int, int start, int end) {
template<bool use_end> void Base<personality>::fetch_tms_refresh(uint8_t, int start, int end) {
RefreshSequencer sequencer(this);
dispatch<use_end>(sequencer, start, end);
}
template <Personality personality>
template<bool use_end> void Base<personality>::fetch_tms_text(LineBuffer &line_buffer, LineBuffer &, int y, int start, int end) {
TextSequencer<personality> sequencer(this, line_buffer, y);
template<bool use_end> void Base<personality>::fetch_tms_text(uint8_t y, int start, int end) {
TextSequencer<personality> sequencer(this, y);
dispatch<use_end>(sequencer, start, end);
}
template <Personality personality>
template<bool use_end> void Base<personality>::fetch_tms_character(LineBuffer &line_buffer, LineBuffer &next_line_buffer, int y, int start, int end) {
CharacterSequencer<personality> sequencer(this, line_buffer, next_line_buffer, y);
template<bool use_end> void Base<personality>::fetch_tms_character(uint8_t y, int start, int end) {
CharacterSequencer<personality> sequencer(this, y);
dispatch<use_end>(sequencer, start, end);
}
@ -575,9 +567,9 @@ struct SMSSequencer {
};
template <Personality personality>
template<bool use_end> void Base<personality>::fetch_sms(LineBuffer &line_buffer, LineBuffer &next_line_buffer, int y, int start, int end) {
template<bool use_end> void Base<personality>::fetch_sms(uint8_t y, int start, int end) {
if constexpr (is_sega_vdp(personality)) {
SMSSequencer<personality> sequencer(this, line_buffer, next_line_buffer, y);
SMSSequencer<personality> sequencer(this, y);
dispatch<use_end>(sequencer, start, end);
}
}
@ -585,10 +577,10 @@ template<bool use_end> void Base<personality>::fetch_sms(LineBuffer &line_buffer
// MARK: - Yamaha
template <Personality personality>
template<ScreenMode mode> void Base<personality>::fetch_yamaha(LineBuffer &line_buffer, LineBuffer &next_line_buffer, int y, int end) {
CharacterFetcher character_fetcher(this, line_buffer, next_line_buffer, y);
TextFetcher text_fetcher(this, line_buffer, y);
SpriteFetcher<personality, sprite_mode(mode)> sprite_fetcher(this, line_buffer, next_line_buffer, y);
template<ScreenMode mode> void Base<personality>::fetch_yamaha(uint8_t y, int end) {
CharacterFetcher character_fetcher(this, y);
TextFetcher text_fetcher(this, y);
SpriteFetcher<personality, sprite_mode(mode)> sprite_fetcher(this, y);
using Type = typename Storage<personality>::Event::Type;
while(Storage<personality>::next_event_->offset < end) {
@ -631,6 +623,7 @@ template<ScreenMode mode> void Base<personality>::fetch_yamaha(LineBuffer &line_
case ScreenMode::YamahaText80: {
const auto column = AddressT(Storage<personality>::next_event_->id);
const auto address = colour_table_address_ & bits<9>(AddressT((y >> 3) * 10));
auto &line_buffer = *fetch_line_buffer_;
line_buffer.characters.flags[column] = ram_[address + column];
} break;
@ -656,6 +649,7 @@ template<ScreenMode mode> void Base<personality>::fetch_yamaha(LineBuffer &line_
case ScreenMode::YamahaText80: {
const auto column = Storage<personality>::next_event_->id << 2;
const auto start = pattern_generator_table_address_ & bits<11>(AddressT(y & 7));
auto &line_buffer = *fetch_line_buffer_;
line_buffer.characters.shapes[column + 0] = ram_[start + AddressT(name_[0] << 3)];
line_buffer.characters.shapes[column + 1] = ram_[start + AddressT(name_[1] << 3)];
@ -682,6 +676,7 @@ template<ScreenMode mode> void Base<personality>::fetch_yamaha(LineBuffer &line_
case ScreenMode::YamahaGraphics5: {
const int column = Storage<personality>::next_event_->id << 2;
const auto start = bits<15>((y << 7) | column);
auto &line_buffer = *fetch_line_buffer_;
line_buffer.bitmap[column + 0] = ram_[pattern_name_address_ & AddressT(start + 0)];
line_buffer.bitmap[column + 1] = ram_[pattern_name_address_ & AddressT(start + 1)];
@ -694,6 +689,7 @@ template<ScreenMode mode> void Base<personality>::fetch_yamaha(LineBuffer &line_
const uint8_t *const ram2 = &ram_[65536];
const int column = Storage<personality>::next_event_->id << 3;
const auto start = bits<15>((y << 7) | (column >> 1));
auto &line_buffer = *fetch_line_buffer_;
// Fetch from alternate banks.
line_buffer.bitmap[column + 0] = ram_[pattern_name_address_ & AddressT(start + 0) & 0xffff];
@ -761,11 +757,11 @@ template<ScreenMode mode> void Base<personality>::fetch_yamaha(LineBuffer &line_
template <Personality personality>
template<bool use_end> void Base<personality>::fetch_yamaha(LineBuffer &line_buffer, LineBuffer &next_line_buffer, int y, int, int end) {
template<bool use_end> void Base<personality>::fetch_yamaha(uint8_t y, int, int end) {
if constexpr (is_yamaha_vdp(personality)) {
// Dispatch according to [supported] screen mode.
#define Dispatch(mode) case mode: fetch_yamaha<mode>(line_buffer, next_line_buffer, y, end); break;
switch(line_buffer.screen_mode) {
#define Dispatch(mode) case mode: fetch_yamaha<mode>(y, end); break;
switch(fetch_line_buffer_->screen_mode) {
default: break;
Dispatch(ScreenMode::Blank);
Dispatch(ScreenMode::Text);

View File

@ -11,8 +11,7 @@
#include "AccessEnums.hpp"
namespace TI {
namespace TMS {
namespace TI::TMS {
// Temporary buffers collect a representation of each line prior to pixel serialisation.
struct LineBuffer {
@ -73,7 +72,9 @@ struct LineBuffer {
int first_pixel_output_column = 94;
int next_border_column = 334;
int pixel_count = 256;
};
struct SpriteBuffer {
// An active sprite is one that has been selected for composition onto
// _this_ line.
struct ActiveSprite {
@ -108,6 +109,7 @@ struct LineBuffer {
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;
void reset_sprite_collection();
};
@ -116,7 +118,6 @@ struct LineBufferPointer {
int row = 0, column = 0;
};
}
}
#endif /* LineBuffer_hpp */