1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-12-26 09:29:45 +00:00

Move towards playfield decoding.

This commit is contained in:
Thomas Harte 2021-08-11 18:47:35 -04:00
parent 635c1eacd5
commit 52e375a985
2 changed files with 90 additions and 35 deletions

View File

@ -12,6 +12,7 @@
#define LOG_PREFIX "[Amiga chipset] " #define LOG_PREFIX "[Amiga chipset] "
#include "../../Outputs/Log.hpp" #include "../../Outputs/Log.hpp"
#include <algorithm>
#include <cassert> #include <cassert>
using namespace Amiga; using namespace Amiga;
@ -368,7 +369,7 @@ template <bool stop_on_cpu> Chipset::Changes Chipset::run(HalfCycles length) {
y_ = 0; y_ = 0;
// TODO: the manual is vague on when this happens. Try to find out. // TODO: the manual is vague on when this happens. Try to find out.
copper_.reload(0); copper_.reload<0>();
} }
} }
assert(line_cycle_ < line_length_ * 4); assert(line_cycle_ < line_length_ * 4);
@ -379,8 +380,18 @@ template <bool stop_on_cpu> Chipset::Changes Chipset::run(HalfCycles length) {
} }
void Chipset::post_bitplanes(const BitplaneData &data) { void Chipset::post_bitplanes(const BitplaneData &data) {
// TODO. // Convert to future pixels.
(void)data; const int odd_offset = line_cycle_ + odd_delay_;
const int even_offset = line_cycle_ + odd_delay_;
for(int x = 0; x < 16; x++) {
const uint16_t mask = uint16_t(1 << x);
even_playfield_[x + even_offset] = uint8_t(
((data[0] & mask) | ((data[2] & mask) << 1) | ((data[4] & mask) << 2)) >> x
);
odd_playfield_[x + odd_offset] = uint8_t(
((data[1] & mask) | ((data[3] & mask) << 1) | ((data[5] & mask) << 2)) >> x
);
}
} }
void Chipset::update_interrupts() { void Chipset::update_interrupts() {
@ -463,8 +474,8 @@ void Chipset::perform(const CPU::MC68000::Microcycle &cycle) {
break; break;
// Disk DMA. // Disk DMA.
case Write(0x020): disk_.set_address<0, 16>(cycle.value16()); break; case Write(0x020): disk_.set_pointer<0, 16>(cycle.value16()); break;
case Write(0x022): disk_.set_address<0, 0>(cycle.value16()); break; case Write(0x022): disk_.set_pointer<0, 0>(cycle.value16()); break;
case Write(0x024): disk_.set_length(cycle.value16()); break; case Write(0x024): disk_.set_length(cycle.value16()); break;
case Write(0x026): case Write(0x026):
@ -540,21 +551,29 @@ void Chipset::perform(const CPU::MC68000::Microcycle &cycle) {
break; break;
// Bitplanes. // Bitplanes.
case Write(0x0e0): bitplanes_.set_address<0, 16>(cycle.value16()); break; case Write(0x0e0): bitplanes_.set_pointer<0, 16>(cycle.value16()); break;
case Write(0x0e2): bitplanes_.set_address<0, 0>(cycle.value16()); break; case Write(0x0e2): bitplanes_.set_pointer<0, 0>(cycle.value16()); break;
case Write(0x0e4): bitplanes_.set_address<1, 16>(cycle.value16()); break; case Write(0x0e4): bitplanes_.set_pointer<1, 16>(cycle.value16()); break;
case Write(0x0e6): bitplanes_.set_address<1, 0>(cycle.value16()); break; case Write(0x0e6): bitplanes_.set_pointer<1, 0>(cycle.value16()); break;
case Write(0x0e8): bitplanes_.set_address<2, 16>(cycle.value16()); break; case Write(0x0e8): bitplanes_.set_pointer<2, 16>(cycle.value16()); break;
case Write(0x0ea): bitplanes_.set_address<2, 0>(cycle.value16()); break; case Write(0x0ea): bitplanes_.set_pointer<2, 0>(cycle.value16()); break;
case Write(0x0ec): bitplanes_.set_address<3, 16>(cycle.value16()); break; case Write(0x0ec): bitplanes_.set_pointer<3, 16>(cycle.value16()); break;
case Write(0x0ee): bitplanes_.set_address<3, 0>(cycle.value16()); break; case Write(0x0ee): bitplanes_.set_pointer<3, 0>(cycle.value16()); break;
case Write(0x0f0): bitplanes_.set_address<4, 16>(cycle.value16()); break; case Write(0x0f0): bitplanes_.set_pointer<4, 16>(cycle.value16()); break;
case Write(0x0f2): bitplanes_.set_address<4, 0>(cycle.value16()); break; case Write(0x0f2): bitplanes_.set_pointer<4, 0>(cycle.value16()); break;
case Write(0x0f4): bitplanes_.set_address<5, 16>(cycle.value16()); break; case Write(0x0f4): bitplanes_.set_pointer<5, 16>(cycle.value16()); break;
case Write(0x0f6): bitplanes_.set_address<5, 0>(cycle.value16()); break; case Write(0x0f6): bitplanes_.set_pointer<5, 0>(cycle.value16()); break;
case Write(0x102): {
const uint8_t delay = cycle.value8_low();
odd_delay_ = delay & 0x0f;
even_delay_ = delay >> 4;
} break;
case Write(0x100): case Write(0x100):
case Write(0x102): bitplanes_.set_control(cycle.value16());
break;
case Write(0x104): case Write(0x104):
case Write(0x106): case Write(0x106):
LOG("TODO: Bitplane control; " << PADHEX(4) << cycle.value16() << " to " << *cycle.address); LOG("TODO: Bitplane control; " << PADHEX(4) << cycle.value16() << " to " << *cycle.address);
@ -629,27 +648,27 @@ void Chipset::perform(const CPU::MC68000::Microcycle &cycle) {
break; break;
case Write(0x080): case Write(0x080):
LOG("Coprocessor first location register high " << PADHEX(4) << cycle.value16()); LOG("Coprocessor first location register high " << PADHEX(4) << cycle.value16());
copper_.set_address<0, 16>(cycle.value16()); copper_.set_pointer<0, 16>(cycle.value16());
break; break;
case Write(0x082): case Write(0x082):
LOG("Coprocessor first location register low " << PADHEX(4) << cycle.value16()); LOG("Coprocessor first location register low " << PADHEX(4) << cycle.value16());
copper_.set_address<0, 0>(cycle.value16()); copper_.set_pointer<0, 0>(cycle.value16());
break; break;
case Write(0x084): case Write(0x084):
LOG("Coprocessor second location register high " << PADHEX(4) << cycle.value16()); LOG("Coprocessor second location register high " << PADHEX(4) << cycle.value16());
copper_.set_address<1, 16>(cycle.value16()); copper_.set_pointer<1, 16>(cycle.value16());
break; break;
case Write(0x086): case Write(0x086):
LOG("Coprocessor second location register low " << PADHEX(4) << cycle.value16()); LOG("Coprocessor second location register low " << PADHEX(4) << cycle.value16());
copper_.set_address<1, 0>(cycle.value16()); copper_.set_pointer<1, 0>(cycle.value16());
break; break;
case Write(0x088): case Read(0x088): case Write(0x088): case Read(0x088):
LOG("Coprocessor restart at first location"); LOG("Coprocessor restart at first location");
copper_.reload(0); copper_.reload<0>();
break; break;
case Write(0x08a): case Read(0x08a): case Write(0x08a): case Read(0x08a):
LOG("Coprocessor restart at second location"); LOG("Coprocessor restart at second location");
copper_.reload(1); copper_.reload<1>();
break; break;
case Write(0x08c): case Write(0x08c):
LOG("TODO: coprocessor instruction fetch identity " << PADHEX(4) << cycle.value16()); LOG("TODO: coprocessor instruction fetch identity " << PADHEX(4) << cycle.value16());
@ -686,9 +705,26 @@ void Chipset::perform(const CPU::MC68000::Microcycle &cycle) {
case Write(0x1b8): case Write(0x1ba): case Write(0x1bc): case Write(0x1be): { case Write(0x1b8): case Write(0x1ba): case Write(0x1bc): case Write(0x1be): {
LOG("Colour palette; " << PADHEX(4) << cycle.value16() << " to " << *cycle.address); LOG("Colour palette; " << PADHEX(4) << cycle.value16() << " to " << *cycle.address);
uint8_t *const entry = reinterpret_cast<uint8_t *>(&palette_[(register_address - 0x180) >> 1]); // Store once in regular, linear order.
const auto entry_address = (register_address - 0x180) >> 1;
uint8_t *const entry = reinterpret_cast<uint8_t *>(&palette_[entry_address]);
entry[0] = cycle.value8_high(); entry[0] = cycle.value8_high();
entry[1] = cycle.value8_low(); entry[1] = cycle.value8_low();
// Also store in bit-swizzled order. In this array,
// instead of being indexed as [b4 b3 b2 b1 b0], index
// as [b3 b1 b4 b2 b0]. This is related to the dual/single-playfield
// decision being made relatively late in the planar -> chunky
// conversion performed by this implementation.
const auto swizzled_address =
(entry_address&0x01) |
((entry_address&0x02) << 2) |
((entry_address&0x04) >> 1) |
((entry_address&0x08) << 1) |
((entry_address&0x10) >> 2);
uint8_t *const swizzled_entry = reinterpret_cast<uint8_t *>(&swizzled_palette_[swizzled_address]);
swizzled_entry[0] = cycle.value8_high();
swizzled_entry[1] = cycle.value8_low();
} break; } break;
} }
@ -714,8 +750,8 @@ template <bool is_odd> bool Chipset::Bitplanes::advance() {
#define BIND_CYCLE(offset, plane) \ #define BIND_CYCLE(offset, plane) \
case (offset + 1)&7: \ case (offset + 1)&7: \
if(plane_count_ > plane) { \ if(plane_count_ > plane) { \
next[plane] = ram_[addresses_[plane] & ram_mask_]; \ next[plane] = ram_[pointer_[plane] & ram_mask_]; \
++addresses_[plane]; \ ++pointer_[plane]; \
if constexpr (!plane) { \ if constexpr (!plane) { \
chipset_.post_bitplanes(next); \ chipset_.post_bitplanes(next); \
} \ } \
@ -762,6 +798,19 @@ void Chipset::Bitplanes::do_end_of_line() {
collection_offset_ = 0; collection_offset_ = 0;
} }
void Chipset::Bitplanes::set_control(uint16_t control) {
is_high_res_ = control & 0x8000;
plane_count_ = (control >> 12) & 7;
// TODO: who really has responsibility for clearing the other
// bit plane fields?
std::fill(next.begin() + plane_count_, next.end(), 0);
if(plane_count_ == 7) {
plane_count_ = 4;
}
}
// MARK: - Sprites. // MARK: - Sprites.
void Chipset::Sprite::set_pointer(int shift, uint16_t value) { void Chipset::Sprite::set_pointer(int shift, uint16_t value) {
@ -788,8 +837,8 @@ bool Chipset::DiskDMA::advance() {
if(!write_) { if(!write_) {
// TODO: run an actual PLL, collect actual disk data. // TODO: run an actual PLL, collect actual disk data.
if(length_) { if(length_) {
ram_[addresses_[0] & ram_mask_] = 0xffff; ram_[pointer_[0] & ram_mask_] = 0xffff;
++addresses_[0]; ++pointer_[0];
--length_; --length_;
if(!length_) { if(!length_) {

View File

@ -112,14 +112,14 @@ class Chipset {
using DMADeviceBase::DMADeviceBase; using DMADeviceBase::DMADeviceBase;
/// Writes the word @c value to the address register @c id, shifting it by @c shift (0 or 16) first. /// Writes the word @c value to the address register @c id, shifting it by @c shift (0 or 16) first.
template <int id, int shift> void set_address(uint16_t value) { template <int id, int shift> void set_pointer(uint16_t value) {
static_assert(id < num_addresses); static_assert(id < num_addresses);
static_assert(shift == 0 || shift == 16); static_assert(shift == 0 || shift == 16);
addresses_[id] = (addresses_[id] & (0xffff'0000 >> shift)) | uint32_t(value << shift); pointer_[id] = (pointer_[id] & (0xffff'0000 >> shift)) | uint32_t(value << shift);
} }
protected: protected:
std::array<uint32_t, num_addresses> addresses_{}; std::array<uint32_t, num_addresses> pointer_{};
}; };
// MARK: - Interrupts. // MARK: - Interrupts.
@ -179,6 +179,7 @@ class Chipset {
template <bool is_odd> bool advance(); template <bool is_odd> bool advance();
void do_end_of_line(); void do_end_of_line();
void set_control(uint16_t);
private: private:
bool is_high_res_ = false; bool is_high_res_ = false;
@ -190,6 +191,10 @@ class Chipset {
void post_bitplanes(const BitplaneData &data); void post_bitplanes(const BitplaneData &data);
uint8_t even_playfield_[912];
uint8_t odd_playfield_[912];
int odd_delay_ = 0, even_delay_ = 0;
// MARK: - Copper. // MARK: - Copper.
class Copper: public DMADevice<2> { class Copper: public DMADevice<2> {
@ -202,8 +207,8 @@ class Chipset {
bool advance(uint16_t position); bool advance(uint16_t position);
/// Forces a reload of address @c id (i.e. 0 or 1) and restarts the Copper. /// Forces a reload of address @c id (i.e. 0 or 1) and restarts the Copper.
void reload(int id) { template <int id> void reload() {
address_ = addresses_[id] >> 1; address_ = pointer_[id] >> 1;
state_ = State::FetchFirstWord; state_ = State::FetchFirstWord;
} }
@ -253,7 +258,7 @@ class Chipset {
length_ = value & 0x3fff; length_ = value & 0x3fff;
if(dma_enable_) { if(dma_enable_) {
printf("Not yet implemented: disk DMA [%s of %d to %06x]\n", write_ ? "write" : "read", length_, addresses_[0]); printf("Not yet implemented: disk DMA [%s of %d to %06x]\n", write_ ? "write" : "read", length_, pointer_[0]);
} }
} }
@ -269,6 +274,7 @@ class Chipset {
Outputs::CRT::CRT crt_; Outputs::CRT::CRT crt_;
uint16_t palette_[32]{}; uint16_t palette_[32]{};
uint16_t swizzled_palette_[32]{};
}; };
} }