1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-07-05 10:28:58 +00:00

Starts on a two-at-a-time sprite shifter.

This commit is contained in:
Thomas Harte 2021-10-25 16:30:30 -07:00
parent 9f3efb7f05
commit cb24457b4a
2 changed files with 119 additions and 58 deletions

View File

@ -31,6 +31,26 @@ template <typename EnumT, EnumT F, EnumT... T> struct Mask<EnumT, F, T...> {
template <InterruptFlag... Flags> struct InterruptMask: Mask<InterruptFlag, Flags...> {};
template <DMAFlag... Flags> struct DMAMask: Mask<DMAFlag, Flags...> {};
/// 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<uint16_t *>(map.chip_ram.data()), map.chip_ram.size() >> 1
@ -179,7 +199,7 @@ template <int cycle> 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 <int cycle> 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 <int sprite> 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) {

View File

@ -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 <int sprite> 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<uint64_t, 2> {
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<uint64_t, 2> data_{};
} bitplane_pixels_;
int odd_delay_ = 0, even_delay_ = 0;