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

Reformulates TMS sprite plotting to set the collision flag and to support magnified sprites.

This commit is contained in:
Thomas Harte 2017-12-09 20:30:12 -05:00
parent 7d7e2538bd
commit 943418c434
2 changed files with 63 additions and 22 deletions

View File

@ -89,7 +89,7 @@ void TMS9918::test_sprite(int sprite_number) {
SpriteSet::ActiveSprite &sprite = sprite_sets_[active_sprite_set_].active_sprites[active_sprite_slot]; SpriteSet::ActiveSprite &sprite = sprite_sets_[active_sprite_set_].active_sprites[active_sprite_slot];
sprite.index = sprite_number; sprite.index = sprite_number;
sprite.row = sprite_row; sprite.row = sprite_row >> (sprites_magnified_ ? 1 : 0);
sprite_sets_[active_sprite_set_].active_sprite_slot++; sprite_sets_[active_sprite_set_].active_sprite_slot++;
} }
@ -128,7 +128,7 @@ void TMS9918::get_sprite_contents(int field, int cycles_left, int screen_row) {
void TMS9918::run_for(const HalfCycles cycles) { void TMS9918::run_for(const HalfCycles cycles) {
// As specific as I've been able to get: // As specific as I've been able to get:
// Scanline time is always 227.75 cycles. // Scanline time is always 228 cycles.
// PAL output is 313 lines total. NTSC output is 262 lines total. // PAL output is 313 lines total. NTSC output is 262 lines total.
// Interrupt is signalled upon entering the lower border. // Interrupt is signalled upon entering the lower border.
@ -351,10 +351,26 @@ void TMS9918::run_for(const HalfCycles cycles) {
} break; } break;
case LineMode::Character: { case LineMode::Character: {
// If this is the start of the visible area, seed sprite shifter positions.
SpriteSet &sprite_set = sprite_sets_[active_sprite_set_ ^ 1];
if(line_mode_ == LineMode::Character && output_column_ == first_pixel_column_) {
int c = sprite_set.active_sprite_slot;
while(c--) {
SpriteSet::ActiveSprite &sprite = sprite_set.active_sprites[c];
sprite.shift_position = -sprite.info[1];
if(sprite.info[3] & 0x80) {
sprite.shift_position += 32;
if(sprite.shift_position > 0 && !sprites_magnified_)
sprite.shift_position *= 2;
}
}
}
// Paint the background tiles.
const int shift = (output_column_ - first_pixel_column_) & 7; const int shift = (output_column_ - first_pixel_column_) & 7;
int byte_column = (output_column_ - first_pixel_column_) >> 3; int byte_column = (output_column_ - first_pixel_column_) >> 3;
int pixels_left = pixels_end - output_column_; const int pixels_left = pixels_end - output_column_;
int length = std::min(pixels_left, 8 - shift); int length = std::min(pixels_left, 8 - shift);
int pattern = pattern_buffer_[byte_column] << shift; int pattern = pattern_buffer_[byte_column] << shift;
@ -364,16 +380,17 @@ void TMS9918::run_for(const HalfCycles cycles) {
(colour >> 4) ? palette[colour >> 4] : background_colour_ (colour >> 4) ? palette[colour >> 4] : background_colour_
}; };
int background_pixels_left = pixels_left;
while(true) { while(true) {
pixels_left -= length; background_pixels_left -= length;
while(length--) { while(length--) {
*pixel_target_ = colours[(pattern >> 7)&0x01]; *pixel_target_ = colours[(pattern >> 7)&0x01];
pixel_target_++; pixel_target_++;
pattern <<= 1; pattern <<= 1;
} }
if(!pixels_left) break; if(!background_pixels_left) break;
length = std::min(8, pixels_left); length = std::min(8, background_pixels_left);
byte_column++; byte_column++;
pattern = pattern_buffer_[byte_column]; pattern = pattern_buffer_[byte_column];
@ -381,27 +398,48 @@ void TMS9918::run_for(const HalfCycles cycles) {
colours[0] = (colour & 15) ? palette[colour & 15] : background_colour_; colours[0] = (colour & 15) ? palette[colour & 15] : background_colour_;
colours[1] = (colour >> 4) ? palette[colour >> 4] : background_colour_; colours[1] = (colour >> 4) ? palette[colour >> 4] : background_colour_;
} }
// Paint sprites and check for collisions.
if(sprite_set.active_sprite_slot) {
int sprite_pixels_left = pixels_left;
const int shift_advance = sprites_magnified_ ? 1 : 2;
// const uint32_t sprite_colour_selection_masks[2] = {0x00000000, 0xffffffff};
while(sprite_pixels_left--) {
uint32_t sprite_colour = pixel_base_[output_column_ - first_pixel_column_];
int sprite_mask = 0;
int c = sprite_set.active_sprite_slot;
while(c--) {
SpriteSet::ActiveSprite &sprite = sprite_set.active_sprites[c];
if(sprite.shift_position < 0) {
sprite.shift_position++;
continue;
} else if(sprite.shift_position < 32) {
int mask = sprite.image[sprite.shift_position >> 4] << ((sprite.shift_position&15) >> 1);
mask = (mask >> 7) & 1;
status_ |= (mask & sprite_mask) << 5;
sprite_mask |= mask;
sprite.shift_position += shift_advance;
// TODO: can a non-conditional version be found like that commented out below, but
// which accounts for colour 0 being invisible?
// sprite_colour = (sprite_colour & sprite_colour_selection_masks[mask^1]) | (palette[sprite.info[3]&15] & sprite_colour_selection_masks[mask]);
if((sprite.info[3]&15) && mask) {
sprite_colour = palette[sprite.info[3]&15];
}
}
}
pixel_base_[output_column_ - first_pixel_column_] = sprite_colour;
output_column_++;
}
}
output_column_ = pixels_end; output_column_ = pixels_end;
} break; } break;
} }
if(output_column_ == first_right_border_column_) { if(output_column_ == first_right_border_column_) {
// Just chuck the sprites on. Quick hack!
int last_sprite_set = active_sprite_set_ ^ 1;
int c = sprite_sets_[last_sprite_set].active_sprite_slot;
while(c--) {
SpriteSet::ActiveSprite &sprite = sprite_sets_[last_sprite_set].active_sprites[c];
if(!(sprite.info[3]&15)) continue;
for(int p = 0; p < (sprites_16x16_ ? 16 : 8); ++p) {
int x = sprite.info[1] + p;
if(sprite.info[3] & 0x80) x -= 32;
if(x >= 0 && x < 256) {
if(((sprite.image[p >> 3] << (p&7)) & 0x80)) pixel_base_[x] = palette[sprite.info[3]&15];
}
}
}
crt_->output_data(static_cast<unsigned int>(first_right_border_column_ - first_pixel_column_), 1); crt_->output_data(static_cast<unsigned int>(first_right_border_column_ - first_pixel_column_), 1);
pixel_target_ = nullptr; pixel_target_ = nullptr;
} }

View File

@ -102,8 +102,11 @@ class TMS9918 {
struct ActiveSprite { struct ActiveSprite {
int index = 0; int index = 0;
int row = 0; int row = 0;
uint8_t info[4]; uint8_t info[4];
uint8_t image[2]; uint8_t image[2];
int shift_position = 0;
} active_sprites[4]; } active_sprites[4];
int active_sprite_slot = 0; int active_sprite_slot = 0;
} sprite_sets_[2]; } sprite_sets_[2];