From 52e375a985b51af433c2b13ff2660633358ae1a6 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 11 Aug 2021 18:47:35 -0400 Subject: [PATCH] Move towards playfield decoding. --- Machines/Amiga/Chipset.cpp | 107 +++++++++++++++++++++++++++---------- Machines/Amiga/Chipset.hpp | 18 ++++--- 2 files changed, 90 insertions(+), 35 deletions(-) diff --git a/Machines/Amiga/Chipset.cpp b/Machines/Amiga/Chipset.cpp index a145708b8..0aaeed41b 100644 --- a/Machines/Amiga/Chipset.cpp +++ b/Machines/Amiga/Chipset.cpp @@ -12,6 +12,7 @@ #define LOG_PREFIX "[Amiga chipset] " #include "../../Outputs/Log.hpp" +#include #include using namespace Amiga; @@ -368,7 +369,7 @@ template Chipset::Changes Chipset::run(HalfCycles length) { y_ = 0; // 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); @@ -379,8 +380,18 @@ template Chipset::Changes Chipset::run(HalfCycles length) { } void Chipset::post_bitplanes(const BitplaneData &data) { - // TODO. - (void)data; + // Convert to future pixels. + 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() { @@ -463,8 +474,8 @@ void Chipset::perform(const CPU::MC68000::Microcycle &cycle) { break; // Disk DMA. - case Write(0x020): disk_.set_address<0, 16>(cycle.value16()); break; - case Write(0x022): disk_.set_address<0, 0>(cycle.value16()); break; + case Write(0x020): disk_.set_pointer<0, 16>(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(0x026): @@ -540,21 +551,29 @@ void Chipset::perform(const CPU::MC68000::Microcycle &cycle) { break; // Bitplanes. - case Write(0x0e0): bitplanes_.set_address<0, 16>(cycle.value16()); break; - case Write(0x0e2): bitplanes_.set_address<0, 0>(cycle.value16()); break; - case Write(0x0e4): bitplanes_.set_address<1, 16>(cycle.value16()); break; - case Write(0x0e6): bitplanes_.set_address<1, 0>(cycle.value16()); break; - case Write(0x0e8): bitplanes_.set_address<2, 16>(cycle.value16()); break; - case Write(0x0ea): bitplanes_.set_address<2, 0>(cycle.value16()); break; - case Write(0x0ec): bitplanes_.set_address<3, 16>(cycle.value16()); break; - case Write(0x0ee): bitplanes_.set_address<3, 0>(cycle.value16()); break; - case Write(0x0f0): bitplanes_.set_address<4, 16>(cycle.value16()); break; - case Write(0x0f2): bitplanes_.set_address<4, 0>(cycle.value16()); break; - case Write(0x0f4): bitplanes_.set_address<5, 16>(cycle.value16()); break; - case Write(0x0f6): bitplanes_.set_address<5, 0>(cycle.value16()); break; + case Write(0x0e0): bitplanes_.set_pointer<0, 16>(cycle.value16()); break; + case Write(0x0e2): bitplanes_.set_pointer<0, 0>(cycle.value16()); break; + case Write(0x0e4): bitplanes_.set_pointer<1, 16>(cycle.value16()); break; + case Write(0x0e6): bitplanes_.set_pointer<1, 0>(cycle.value16()); break; + case Write(0x0e8): bitplanes_.set_pointer<2, 16>(cycle.value16()); break; + case Write(0x0ea): bitplanes_.set_pointer<2, 0>(cycle.value16()); break; + case Write(0x0ec): bitplanes_.set_pointer<3, 16>(cycle.value16()); break; + case Write(0x0ee): bitplanes_.set_pointer<3, 0>(cycle.value16()); break; + case Write(0x0f0): bitplanes_.set_pointer<4, 16>(cycle.value16()); break; + case Write(0x0f2): bitplanes_.set_pointer<4, 0>(cycle.value16()); break; + case Write(0x0f4): bitplanes_.set_pointer<5, 16>(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(0x102): + bitplanes_.set_control(cycle.value16()); + break; + case Write(0x104): case Write(0x106): LOG("TODO: Bitplane control; " << PADHEX(4) << cycle.value16() << " to " << *cycle.address); @@ -629,27 +648,27 @@ void Chipset::perform(const CPU::MC68000::Microcycle &cycle) { break; case Write(0x080): 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; case Write(0x082): 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; case Write(0x084): 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; case Write(0x086): 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; case Write(0x088): case Read(0x088): LOG("Coprocessor restart at first location"); - copper_.reload(0); + copper_.reload<0>(); break; case Write(0x08a): case Read(0x08a): LOG("Coprocessor restart at second location"); - copper_.reload(1); + copper_.reload<1>(); break; case Write(0x08c): 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): { LOG("Colour palette; " << PADHEX(4) << cycle.value16() << " to " << *cycle.address); - uint8_t *const entry = reinterpret_cast(&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(&palette_[entry_address]); entry[0] = cycle.value8_high(); 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(&swizzled_palette_[swizzled_address]); + swizzled_entry[0] = cycle.value8_high(); + swizzled_entry[1] = cycle.value8_low(); } break; } @@ -714,8 +750,8 @@ template bool Chipset::Bitplanes::advance() { #define BIND_CYCLE(offset, plane) \ case (offset + 1)&7: \ if(plane_count_ > plane) { \ - next[plane] = ram_[addresses_[plane] & ram_mask_]; \ - ++addresses_[plane]; \ + next[plane] = ram_[pointer_[plane] & ram_mask_]; \ + ++pointer_[plane]; \ if constexpr (!plane) { \ chipset_.post_bitplanes(next); \ } \ @@ -762,6 +798,19 @@ void Chipset::Bitplanes::do_end_of_line() { 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. void Chipset::Sprite::set_pointer(int shift, uint16_t value) { @@ -788,8 +837,8 @@ bool Chipset::DiskDMA::advance() { if(!write_) { // TODO: run an actual PLL, collect actual disk data. if(length_) { - ram_[addresses_[0] & ram_mask_] = 0xffff; - ++addresses_[0]; + ram_[pointer_[0] & ram_mask_] = 0xffff; + ++pointer_[0]; --length_; if(!length_) { diff --git a/Machines/Amiga/Chipset.hpp b/Machines/Amiga/Chipset.hpp index e81ae0f66..346848203 100644 --- a/Machines/Amiga/Chipset.hpp +++ b/Machines/Amiga/Chipset.hpp @@ -112,14 +112,14 @@ class Chipset { using DMADeviceBase::DMADeviceBase; /// Writes the word @c value to the address register @c id, shifting it by @c shift (0 or 16) first. - template void set_address(uint16_t value) { + template void set_pointer(uint16_t value) { static_assert(id < num_addresses); 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: - std::array addresses_{}; + std::array pointer_{}; }; // MARK: - Interrupts. @@ -179,6 +179,7 @@ class Chipset { template bool advance(); void do_end_of_line(); + void set_control(uint16_t); private: bool is_high_res_ = false; @@ -190,6 +191,10 @@ class Chipset { 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. class Copper: public DMADevice<2> { @@ -202,8 +207,8 @@ class Chipset { bool advance(uint16_t position); /// Forces a reload of address @c id (i.e. 0 or 1) and restarts the Copper. - void reload(int id) { - address_ = addresses_[id] >> 1; + template void reload() { + address_ = pointer_[id] >> 1; state_ = State::FetchFirstWord; } @@ -253,7 +258,7 @@ class Chipset { length_ = value & 0x3fff; 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_; uint16_t palette_[32]{}; + uint16_t swizzled_palette_[32]{}; }; }