diff --git a/Machines/Amiga/Chipset.cpp b/Machines/Amiga/Chipset.cpp index 2d7ba3084..f355d0527 100644 --- a/Machines/Amiga/Chipset.cpp +++ b/Machines/Amiga/Chipset.cpp @@ -31,6 +31,26 @@ template struct Mask { template struct InterruptMask: Mask {}; template struct DMAMask: Mask {}; +/// Expands @c source so that b7 is the least-significant bit of the most-significant byte of the result, +/// b6 is the least-significant bit of the next most significant byte, etc. b0 stays in place. +constexpr uint64_t expand_bitplane_byte(uint8_t source) { + uint64_t result = source; // 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 abcd efgh + result = (result | (result << 28)) & 0x0000'000f'0000'000f; // 0000 0000 0000 0000 0000 0000 0000 abcd 0000 0000 0000 0000 0000 0000 0000 efgh + result = (result | (result << 14)) & 0x0003'0003'0003'0003; // 0000 0000 0000 00ab 0000 0000 0000 00cd 0000 0000 0000 00ef 0000 0000 0000 00gh + result = (result | (result << 7)) & 0x0101'0101'0101'0101; // 0000 000a 0000 000b 0000 000c 0000 000d 0000 000e 0000 000f 0000 000g 0000 000h + return result; +} + +// A very small selection of test cases. +static_assert(expand_bitplane_byte(0xff) == 0x01'01'01'01'01'01'01'01); +static_assert(expand_bitplane_byte(0x55) == 0x00'01'00'01'00'01'00'01); +static_assert(expand_bitplane_byte(0xaa) == 0x01'00'01'00'01'00'01'00); +static_assert(expand_bitplane_byte(0x00) == 0x00'00'00'00'00'00'00'00); + +constexpr uint64_t expand_sprite_word(uint16_t source) { + return 0; // TODO. +} + } #define DMA_CONSTRUCT *this, reinterpret_cast(map.chip_ram.data()), map.chip_ram.size() >> 1 @@ -179,7 +199,7 @@ template void Chipset::output() { if(pixels_) { // TODO: this doesn't support dual playfields; use an alternative - // palette table for that. + // palette table for that? const uint32_t source = bitplane_pixels_.get(is_high_res_); pixels_[0] = palette_[source >> 24]; @@ -230,8 +250,11 @@ template void Chipset::output() { } // Update all active pixel shifters. - // TODO: including sprites. bitplane_pixels_.shift(is_high_res_); + sprite_shifters_[0].shift(); + sprite_shifters_[1].shift(); + sprite_shifters_[2].shift(); + sprite_shifters_[3].shift(); } } @@ -481,27 +504,7 @@ void Chipset::post_bitplanes(const BitplaneData &data) { fetch_horizontal_ &= !horizontal_is_last_; } -namespace { - -/// Expands @c source so that b7 is the least-significant bit of the most-significant byte of the result, -/// b6 is the least-significant bit of the next most significant byte, etc. b0 stays in place. -constexpr uint64_t expand_byte(uint8_t source) { - uint64_t result = source; // 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 abcd efgh - result = (result | (result << 28)) & 0x0000'000f'0000'000f; // 0000 0000 0000 0000 0000 0000 0000 abcd 0000 0000 0000 0000 0000 0000 0000 efgh - result = (result | (result << 14)) & 0x0003'0003'0003'0003; // 0000 0000 0000 00ab 0000 0000 0000 00cd 0000 0000 0000 00ef 0000 0000 0000 00gh - result = (result | (result << 7)) & 0x0101'0101'0101'0101; // 0000 000a 0000 000b 0000 000c 0000 000d 0000 000e 0000 000f 0000 000g 0000 000h - return result; -} - -// A very small selection of test cases. -static_assert(expand_byte(0xff) == 0x01'01'01'01'01'01'01'01); -static_assert(expand_byte(0x55) == 0x00'01'00'01'00'01'00'01); -static_assert(expand_byte(0xaa) == 0x01'00'01'00'01'00'01'00); -static_assert(expand_byte(0x00) == 0x00'00'00'00'00'00'00'00); - -} - -void Chipset::SixteenPixels::set(const BitplaneData &previous, const BitplaneData &next, int odd_delay, int even_delay) { +void Chipset::BitplaneShifter::set(const BitplaneData &previous, const BitplaneData &next, int odd_delay, int even_delay) { const uint16_t planes[6] = { uint16_t(((previous[0] << 16) | next[0]) >> even_delay), uint16_t(((previous[1] << 16) | next[1]) >> odd_delay), @@ -511,21 +514,21 @@ void Chipset::SixteenPixels::set(const BitplaneData &previous, const BitplaneDat uint16_t(((previous[5] << 16) | next[5]) >> odd_delay), }; - (*this)[0] = - (expand_byte(uint8_t(planes[0])) << 0) | - (expand_byte(uint8_t(planes[1])) << 1) | - (expand_byte(uint8_t(planes[2])) << 2) | - (expand_byte(uint8_t(planes[3])) << 3) | - (expand_byte(uint8_t(planes[4])) << 4) | - (expand_byte(uint8_t(planes[5])) << 5); + data_[0] = + (expand_bitplane_byte(uint8_t(planes[0])) << 0) | + (expand_bitplane_byte(uint8_t(planes[1])) << 1) | + (expand_bitplane_byte(uint8_t(planes[2])) << 2) | + (expand_bitplane_byte(uint8_t(planes[3])) << 3) | + (expand_bitplane_byte(uint8_t(planes[4])) << 4) | + (expand_bitplane_byte(uint8_t(planes[5])) << 5); - (*this)[1] = - (expand_byte(uint8_t(planes[0] >> 8)) << 0) | - (expand_byte(uint8_t(planes[1] >> 8)) << 1) | - (expand_byte(uint8_t(planes[2] >> 8)) << 2) | - (expand_byte(uint8_t(planes[3] >> 8)) << 3) | - (expand_byte(uint8_t(planes[4] >> 8)) << 4) | - (expand_byte(uint8_t(planes[5] >> 8)) << 5); + data_[1] = + (expand_bitplane_byte(uint8_t(planes[0] >> 8)) << 0) | + (expand_bitplane_byte(uint8_t(planes[1] >> 8)) << 1) | + (expand_bitplane_byte(uint8_t(planes[2] >> 8)) << 2) | + (expand_bitplane_byte(uint8_t(planes[3] >> 8)) << 3) | + (expand_bitplane_byte(uint8_t(planes[4] >> 8)) << 4) | + (expand_bitplane_byte(uint8_t(planes[5] >> 8)) << 5); } void Chipset::update_interrupts() { @@ -1023,6 +1026,24 @@ void Chipset::Sprite::reset_dma() { dma_state_ = DMAState::FetchStart; } +template void Chipset::TwoSpriteShifter::load( + uint16_t lsb, + uint16_t msb, + int delay) { + constexpr int sprite_shift = sprite << 1; + const int delay_shift = delay << 2; + + // Clear out any current sprite pixels. + data_ &= (0x3333'3333'3333'3333 << sprite_shift) >> delay_shift; + + // Map LSB and MSB up to 64-bits and load into the shifter. + const uint64_t new_data = + (expand_sprite_word(lsb) | (expand_sprite_word(msb) << 1)) << sprite_shift; + data_ |= new_data >> delay_shift; + + overflow_ |= new_data >> (64 - delay_shift); +} + // MARK: - Disk. void Chipset::DiskDMA::enqueue(uint16_t value, bool matches_sync) { diff --git a/Machines/Amiga/Chipset.hpp b/Machines/Amiga/Chipset.hpp index ec14b5ddb..4b9bf1b9c 100644 --- a/Machines/Amiga/Chipset.hpp +++ b/Machines/Amiga/Chipset.hpp @@ -157,6 +157,36 @@ class Chipset: private ClockingHint::Observer { } dma_state_ = DMAState::FetchStart; } sprites_[8]; + class TwoSpriteShifter { + public: + /// Installs new pixel data for @c sprite (either 0 or 1), + /// with @c delay being either 0 or 1 to indicate whether + /// output should begin now or in one pixel's time. + template void load( + uint16_t lsb, + uint16_t msb, + int delay); + + /// Shifts two pixels. + void shift() { + data_ <<= 8; + data_ |= overflow_; + overflow_ = 0; + } + + /// @returns The next two pixels to output, formulated as: + /// abcd efgh where ab and ef are two pixels of the first sprite + /// and cd and gh are two pixels of the second. In each case the + /// more significant two are output first. + uint8_t get() { + return uint8_t(data_ >> 24); + } + + private: + uint64_t data_; + uint8_t overflow_; + } sprite_shifters_[4]; + // MARK: - Raster position and state. // Definitions related to PAL/NTSC. @@ -219,30 +249,40 @@ class Chipset: private ClockingHint::Observer { void post_bitplanes(const BitplaneData &data); BitplaneData previous_bitplanes_; - struct SixteenPixels: public std::array { - void set( - const BitplaneData &previous, - const BitplaneData &next, - int odd_delay, - int even_delay); + class BitplaneShifter { + public: + /// Installs a new set of output pixels. + void set( + const BitplaneData &previous, + const BitplaneData &next, + int odd_delay, + int even_delay); - void shift(bool high_res) { - constexpr int shifts[] = {16, 32}; + /// Shifts either two pixels (in low-res mode) and four pixels (in high-res). + void shift(bool high_res) { + constexpr int shifts[] = {16, 32}; - (*this)[1] = ((*this)[1] << shifts[high_res]) | ((*this)[0] >> (64 - shifts[high_res])); - (*this)[0] <<= shifts[high_res]; - } - - uint32_t get(bool high_res) { - if(high_res) { - return uint32_t((*this)[1] >> 32); - } else { - uint32_t result = uint16_t((*this)[1] >> 48); - result = ((result & 0xff00) << 8) | (result & 0x00ff); - result |= result << 8; - return result; + data_[1] = (data_[1] << shifts[high_res]) | (data_[0] >> (64 - shifts[high_res])); + data_[0] <<= shifts[high_res]; } - } + + /// @returns The next four pixels to output; in low-resolution mode only two + /// of them will be unique. The value is arranges so that MSB = first pixel to output, + /// LSB = last. Each byte is formed as 00[bitplane 5][bitplane 4]...[bitplane 0]. + uint32_t get(bool high_res) { + if(high_res) { + return uint32_t(data_[1] >> 32); + } else { + uint32_t result = uint16_t(data_[1] >> 48); + result = ((result & 0xff00) << 8) | (result & 0x00ff); + result |= result << 8; + return result; + } + } + + private: + std::array data_{}; + } bitplane_pixels_; int odd_delay_ = 0, even_delay_ = 0;