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:
parent
de3cd9c286
commit
931d2373a4
@ -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;
|
||||
|
@ -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"
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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 */
|
||||
|
Loading…
Reference in New Issue
Block a user