1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-12-26 09:29:45 +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];
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<unsigned int>(first_right_border_column_ - first_pixel_column_), 1);
pixel_target_ = nullptr;
}

View File

@ -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];