From 943418c4343643bb08665f4171e62239cc3f5200 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 9 Dec 2017 20:30:12 -0500 Subject: [PATCH] Reformulates TMS sprite plotting to set the collision flag and to support magnified sprites. --- Components/9918/9918.cpp | 82 +++++++++++++++++++++++++++++----------- Components/9918/9918.hpp | 3 ++ 2 files changed, 63 insertions(+), 22 deletions(-) diff --git a/Components/9918/9918.cpp b/Components/9918/9918.cpp index 62c0e91e5..5e4f449c7 100644 --- a/Components/9918/9918.cpp +++ b/Components/9918/9918.cpp @@ -89,7 +89,7 @@ void TMS9918::test_sprite(int sprite_number) { SpriteSet::ActiveSprite &sprite = sprite_sets_[active_sprite_set_].active_sprites[active_sprite_slot]; sprite.index = sprite_number; - sprite.row = sprite_row; + sprite.row = sprite_row >> (sprites_magnified_ ? 1 : 0); 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) { // 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. // Interrupt is signalled upon entering the lower border. @@ -351,10 +351,26 @@ void TMS9918::run_for(const HalfCycles cycles) { } break; 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; 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 pattern = pattern_buffer_[byte_column] << shift; @@ -364,16 +380,17 @@ void TMS9918::run_for(const HalfCycles cycles) { (colour >> 4) ? palette[colour >> 4] : background_colour_ }; + int background_pixels_left = pixels_left; while(true) { - pixels_left -= length; + background_pixels_left -= length; while(length--) { *pixel_target_ = colours[(pattern >> 7)&0x01]; pixel_target_++; pattern <<= 1; } - if(!pixels_left) break; - length = std::min(8, pixels_left); + if(!background_pixels_left) break; + length = std::min(8, background_pixels_left); 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[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; } break; } 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(first_right_border_column_ - first_pixel_column_), 1); pixel_target_ = nullptr; } diff --git a/Components/9918/9918.hpp b/Components/9918/9918.hpp index b0d5fadfb..44d908f16 100644 --- a/Components/9918/9918.hpp +++ b/Components/9918/9918.hpp @@ -102,8 +102,11 @@ class TMS9918 { struct ActiveSprite { int index = 0; int row = 0; + uint8_t info[4]; uint8_t image[2]; + + int shift_position = 0; } active_sprites[4]; int active_sprite_slot = 0; } sprite_sets_[2];