mirror of
https://github.com/TomHarte/CLK.git
synced 2025-01-27 06:35:04 +00:00
Switches to basic use of sprite shifters.
This commit is contained in:
parent
cb24457b4a
commit
139d35c6f9
@ -41,15 +41,26 @@ constexpr uint64_t expand_bitplane_byte(uint8_t source) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Expands @c source from b15 ... b0 to 000b15 ... 000b0.
|
||||||
|
constexpr uint64_t expand_sprite_word(uint16_t source) {
|
||||||
|
uint64_t result = source;
|
||||||
|
result = (result | (result << 24)) & 0x0000'00ff'0000'00ff;
|
||||||
|
result = (result | (result << 12)) & 0x000f'000f'000f'000f;
|
||||||
|
result = (result | (result << 6)) & 0x0303'0303'0303'0303;
|
||||||
|
result = (result | (result << 3)) & 0x1111'1111'1111'1111;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
// A very small selection of test cases.
|
// 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(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(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(0xaa) == 0x01'00'01'00'01'00'01'00);
|
||||||
static_assert(expand_bitplane_byte(0x00) == 0x00'00'00'00'00'00'00'00);
|
static_assert(expand_bitplane_byte(0x00) == 0x00'00'00'00'00'00'00'00);
|
||||||
|
|
||||||
constexpr uint64_t expand_sprite_word(uint16_t source) {
|
static_assert(expand_sprite_word(0xffff) == 0x11'11'11'11'11'11'11'11);
|
||||||
return 0; // TODO.
|
static_assert(expand_sprite_word(0x5555) == 0x01'01'01'01'01'01'01'01);
|
||||||
}
|
static_assert(expand_sprite_word(0xaaaa) == 0x10'10'10'10'10'10'10'10);
|
||||||
|
static_assert(expand_sprite_word(0x0000) == 0x00'00'00'00'00'00'00'00);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -127,6 +138,28 @@ template <int cycle> void Chipset::output() {
|
|||||||
constexpr int blank3 = 7 + burst;
|
constexpr int blank3 = 7 + burst;
|
||||||
static_assert(blank3 == 43);
|
static_assert(blank3 == 43);
|
||||||
|
|
||||||
|
// Trigger any sprite loads encountered.
|
||||||
|
constexpr auto dcycle = cycle << 1;
|
||||||
|
for(int c = 0; c < 8; c += 2) {
|
||||||
|
if( sprites_[c].active &&
|
||||||
|
dcycle <= sprites_[c].h_start &&
|
||||||
|
dcycle+2 > sprites_[c].h_start) {
|
||||||
|
sprite_shifters_[c >> 1].load<0>(
|
||||||
|
sprites_[c].data[1],
|
||||||
|
sprites_[c].data[0],
|
||||||
|
sprites_[c].h_start & 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if( sprites_[c+1].active &&
|
||||||
|
dcycle <= sprites_[c + 1].h_start &&
|
||||||
|
dcycle+2 > sprites_[c + 1].h_start) {
|
||||||
|
sprite_shifters_[c >> 1].load<1>(
|
||||||
|
sprites_[c + 1].data[1],
|
||||||
|
sprites_[c + 1].data[0],
|
||||||
|
sprites_[c + 1].h_start & 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#define LINK(location, action, length) \
|
#define LINK(location, action, length) \
|
||||||
if(cycle == (location)) { \
|
if(cycle == (location)) { \
|
||||||
crt_.action((length) * 4); \
|
crt_.action((length) * 4); \
|
||||||
@ -207,35 +240,25 @@ template <int cycle> void Chipset::output() {
|
|||||||
pixels_[2] = palette_[(source >> 8) & 0xff];
|
pixels_[2] = palette_[(source >> 8) & 0xff];
|
||||||
pixels_[3] = palette_[source & 0xff];
|
pixels_[3] = palette_[source & 0xff];
|
||||||
|
|
||||||
// QUICK HACK: dump sprite pixels:
|
for(int c = 3; c >= 0; --c) {
|
||||||
//
|
const auto data = sprite_shifters_[c].get();
|
||||||
// (i) always on top, regardless of current priority;
|
if(!data) continue;
|
||||||
// (ii) assuming four-colour sprites; and
|
const int base = (c << 2) + 16;
|
||||||
// (iii) not using the proper triggering mechanism.
|
|
||||||
//
|
|
||||||
// (and assuming visible area is a subset of the fetch area, but elsewhere
|
|
||||||
// the two are currently equated, so...)
|
|
||||||
constexpr auto dcycle = cycle << 1;
|
|
||||||
for(int c = 7; c >= 0; --c) {
|
|
||||||
if(sprites_[c].active && sprites_[c].h_start_ <= (dcycle+1) && sprites_[c].h_start_+16 >= dcycle) {
|
|
||||||
const int shift = 16 + dcycle - sprites_[c].h_start_;
|
|
||||||
uint32_t pixels[] = {
|
|
||||||
uint32_t(sprites_[c].data[0] << shift),
|
|
||||||
uint32_t(sprites_[c].data[1] << shift),
|
|
||||||
};
|
|
||||||
|
|
||||||
const int colours[] = {
|
// TODO: can do a better job of selection here —
|
||||||
int((pixels[1] >> 31) | ((pixels[0] >> 30) & 2)),
|
// treat each 4-bit quantity as a single colour
|
||||||
int(((pixels[1] >> 30)&1) | ((pixels[0] >> 29) & 2))
|
// selection, much like dual playfield mode.
|
||||||
};
|
if(data >> 6) {
|
||||||
|
pixels_[0] = pixels_[1] = palette_[base + (data >> 6)];
|
||||||
const int base = ((c&~1) << 1) + 16;
|
}
|
||||||
if(colours[0]) {
|
if((data >> 4) & 3) {
|
||||||
pixels_[0] = pixels_[1] = palette_[base + colours[0]];
|
pixels_[0] = pixels_[1] = palette_[base + ((data >> 4)&3)];
|
||||||
}
|
}
|
||||||
if(colours[1]) {
|
if((data >> 2) & 3) {
|
||||||
pixels_[2] = pixels_[3] = palette_[base + colours[1]];
|
pixels_[2] = pixels_[3] = palette_[base + ((data >> 2)&3)];
|
||||||
}
|
}
|
||||||
|
if(data & 3) {
|
||||||
|
pixels_[2] = pixels_[3] = palette_[base + (data & 3)];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -958,12 +981,12 @@ void Chipset::Bitplanes::set_control(uint16_t control) {
|
|||||||
|
|
||||||
void Chipset::Sprite::set_start_position(uint16_t value) {
|
void Chipset::Sprite::set_start_position(uint16_t value) {
|
||||||
v_start_ = (v_start_ & 0xff00) | (value >> 8);
|
v_start_ = (v_start_ & 0xff00) | (value >> 8);
|
||||||
h_start_ = uint16_t((h_start_ & 0x0001) | ((value & 0xff) << 1));
|
h_start = uint16_t((h_start & 0x0001) | ((value & 0xff) << 1));
|
||||||
active = false;
|
active = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Chipset::Sprite::set_stop_and_control(uint16_t value) {
|
void Chipset::Sprite::set_stop_and_control(uint16_t value) {
|
||||||
h_start_ = uint16_t((h_start_ & 0x01fe) | (value & 0x01));
|
h_start = uint16_t((h_start & 0x01fe) | (value & 0x01));
|
||||||
v_stop_ = uint16_t((value >> 8) | ((value & 0x02) << 7));
|
v_stop_ = uint16_t((value >> 8) | ((value & 0x02) << 7));
|
||||||
v_start_ = uint16_t((v_start_ & 0x00ff) | ((value & 0x04) << 6));
|
v_start_ = uint16_t((v_start_ & 0x00ff) | ((value & 0x04) << 6));
|
||||||
attached = value & 0x80;
|
attached = value & 0x80;
|
||||||
@ -1033,15 +1056,18 @@ template <int sprite> void Chipset::TwoSpriteShifter::load(
|
|||||||
constexpr int sprite_shift = sprite << 1;
|
constexpr int sprite_shift = sprite << 1;
|
||||||
const int delay_shift = delay << 2;
|
const int delay_shift = delay << 2;
|
||||||
|
|
||||||
// Clear out any current sprite pixels.
|
// Clear out any current sprite pixels; this is a reload.
|
||||||
data_ &= (0x3333'3333'3333'3333 << sprite_shift) >> delay_shift;
|
data_ &= 0xcccc'cccc'cccc'ccccull >> (sprite_shift + delay_shift);
|
||||||
|
|
||||||
// Map LSB and MSB up to 64-bits and load into the shifter.
|
// Map LSB and MSB up to 64-bits and load into the shifter.
|
||||||
const uint64_t new_data =
|
const uint64_t new_data =
|
||||||
(expand_sprite_word(lsb) | (expand_sprite_word(msb) << 1)) << sprite_shift;
|
(
|
||||||
data_ |= new_data >> delay_shift;
|
expand_sprite_word(lsb) |
|
||||||
|
(expand_sprite_word(msb) << 1)
|
||||||
|
) << sprite_shift;
|
||||||
|
|
||||||
overflow_ |= new_data >> (64 - delay_shift);
|
data_ |= new_data >> delay_shift;
|
||||||
|
overflow_ |= uint8_t((new_data << 8) >> delay_shift);
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Disk.
|
// MARK: - Disk.
|
||||||
|
@ -139,10 +139,7 @@ class Chipset: private ClockingHint::Observer {
|
|||||||
uint16_t data[2]{};
|
uint16_t data[2]{};
|
||||||
bool attached = false;
|
bool attached = false;
|
||||||
bool active = false;
|
bool active = false;
|
||||||
|
uint16_t h_start = 0;
|
||||||
// TODO: unexpose this. It's public temporarily to allow
|
|
||||||
// an initial quick hack of sprite display.
|
|
||||||
uint16_t h_start_ = 0;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint16_t v_start_ = 0, v_stop_ = 0;
|
uint16_t v_start_ = 0, v_stop_ = 0;
|
||||||
@ -174,12 +171,12 @@ class Chipset: private ClockingHint::Observer {
|
|||||||
overflow_ = 0;
|
overflow_ = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @returns The next two pixels to output, formulated as:
|
/// @returns The next two pixels to output, formulated as
|
||||||
/// abcd efgh where ab and ef are two pixels of the first sprite
|
/// 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
|
/// and cd and gh are two pixels of the second. In each case the
|
||||||
/// more significant two are output first.
|
/// more significant two are output first.
|
||||||
uint8_t get() {
|
uint8_t get() {
|
||||||
return uint8_t(data_ >> 24);
|
return uint8_t(data_ >> 56);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user