diff --git a/Machines/Amiga/Chipset.cpp b/Machines/Amiga/Chipset.cpp index e86e07dc2..a145708b8 100644 --- a/Machines/Amiga/Chipset.cpp +++ b/Machines/Amiga/Chipset.cpp @@ -33,6 +33,7 @@ template struct DMAMask: Mask {}; Chipset::Chipset(uint16_t *ram, size_t size) : blitter_(ram, size), + bitplanes_(*this, ram, size), copper_(*this, ram, size), disk_(*this, ram, size), crt_(908, 4, Outputs::Display::Type::PAL50, Outputs::Display::InputDataType::Red4Green4Blue4) { @@ -231,9 +232,13 @@ template bool Chipset::perform_cycle() { fetch_horizontal_ &= cycle != display_window_stop_[0]; // Top priority: bitplane collection. - if(fetch_horizontal_ && fetch_vertical_ && (dma_control_ & BitplaneFlag) == BitplaneFlag) { + if((dma_control_ & BitplaneFlag) == BitplaneFlag) { // TODO: offer a cycle for bitplane collection. // Probably need to indicate odd or even? + if(fetch_horizontal_ && fetch_vertical_ && bitplanes_.template advance()) { + did_fetch_ = true; + return false; + } } if constexpr (cycle & 1) { @@ -348,6 +353,13 @@ template Chipset::Changes Chipset::run(HalfCycles length) { fetch_vertical_ |= y_ == display_window_start_[1]; fetch_vertical_ &= y_ != display_window_stop_[1]; + if(did_fetch_) { + // TODO: find out when modulos are actually applied, since + // they're dynamically programmable. + bitplanes_.do_end_of_line(); + did_fetch_ = false; + } + if(y_ == frame_height_) { ++changes.vsyncs; interrupt_requests_ |= InterruptMask::value; @@ -366,6 +378,11 @@ template Chipset::Changes Chipset::run(HalfCycles length) { return changes; } +void Chipset::post_bitplanes(const BitplaneData &data) { + // TODO. + (void)data; +} + void Chipset::update_interrupts() { interrupt_level_ = 0; @@ -446,9 +463,9 @@ void Chipset::perform(const CPU::MC68000::Microcycle &cycle) { break; // Disk DMA. - case Write(0x020): disk_.set_address<16>(cycle.value16()); break; - case Write(0x022): disk_.set_address<0>(cycle.value16()); break; - case Write(0x024): disk_.set_length(cycle.value16()); break; + case Write(0x020): disk_.set_address<0, 16>(cycle.value16()); break; + case Write(0x022): disk_.set_address<0, 0>(cycle.value16()); break; + case Write(0x024): disk_.set_length(cycle.value16()); break; case Write(0x026): LOG("TODO: disk DMA; " << PADHEX(4) << cycle.value16() << " to " << *cycle.address); @@ -523,14 +540,18 @@ void Chipset::perform(const CPU::MC68000::Microcycle &cycle) { break; // Bitplanes. - case Write(0x0e0): case Write(0x0e2): - case Write(0x0e4): case Write(0x0e6): - case Write(0x0e8): case Write(0x0ea): - case Write(0x0ec): case Write(0x0ee): - case Write(0x0f0): case Write(0x0f2): - case Write(0x0f4): case Write(0x0f6): - LOG("TODO: Bitplane pointer; " << PADHEX(4) << cycle.value16() << " to " << *cycle.address); - break; + 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(0x100): case Write(0x102): @@ -678,6 +699,69 @@ void Chipset::perform(const CPU::MC68000::Microcycle &cycle) { #undef RW } +// MARK: - Bitplanes. + +template bool Chipset::Bitplanes::advance() { + // Offset is preincremented in order to simplify the exit + // pathways below. As a result all in-switch offsets are + // off by 1. This is dealt with in the macro. + ++collection_offset_; + + // TODO: possibly dispatch a BitplaneData. + // The chipset has responsibility for applying a delay, + // and the pixel-level start/stop boundaries. + +#define BIND_CYCLE(offset, plane) \ + case (offset + 1)&7: \ + if(plane_count_ > plane) { \ + next[plane] = ram_[addresses_[plane] & ram_mask_]; \ + ++addresses_[plane]; \ + if constexpr (!plane) { \ + chipset_.post_bitplanes(next); \ + } \ + return true; \ + } \ + return false; + + if(is_high_res_) { + // TODO: I'm unclear whether this is correct, or merely + // an artefact of the way the Hardware Reference Manual + // depicts per-line DMA responsibilities. + if(collection_offset_ < 4) { + return false; + } + + switch(collection_offset_&7) { + default: return false; + BIND_CYCLE(0, 3); + BIND_CYCLE(1, 1); + BIND_CYCLE(2, 2); + BIND_CYCLE(3, 0); + BIND_CYCLE(4, 3); + BIND_CYCLE(5, 1); + BIND_CYCLE(6, 2); + BIND_CYCLE(7, 0); + } + } else { + switch(collection_offset_&7) { + default: return false; + BIND_CYCLE(1, 3); + BIND_CYCLE(2, 5); + BIND_CYCLE(3, 1); + BIND_CYCLE(5, 2); + BIND_CYCLE(6, 4); + BIND_CYCLE(7, 0); + } + } + + return false; +} + +void Chipset::Bitplanes::do_end_of_line() { + // TODO: apply modulos. + collection_offset_ = 0; +} + // MARK: - Sprites. void Chipset::Sprite::set_pointer(int shift, uint16_t value) { @@ -704,8 +788,8 @@ bool Chipset::DiskDMA::advance() { if(!write_) { // TODO: run an actual PLL, collect actual disk data. if(length_) { - ram_[address_ & ram_mask_] = 0xffff; - ++address_; + ram_[addresses_[0] & ram_mask_] = 0xffff; + ++addresses_[0]; --length_; if(!length_) { diff --git a/Machines/Amiga/Chipset.hpp b/Machines/Amiga/Chipset.hpp index f0644773f..e81ae0f66 100644 --- a/Machines/Amiga/Chipset.hpp +++ b/Machines/Amiga/Chipset.hpp @@ -96,16 +96,31 @@ class Chipset { private: // MARK: - Common base for DMA components. - class DMADevice { + class DMADeviceBase { public: - DMADevice(Chipset &chipset, uint16_t *ram, size_t size) : chipset_(chipset), ram_(ram), ram_mask_(uint32_t(size - 1)) {} + DMADeviceBase(Chipset &chipset, uint16_t *ram, size_t size) : chipset_(chipset), ram_(ram), ram_mask_(uint32_t(size - 1)) {} protected: Chipset &chipset_; uint16_t *ram_ = nullptr; uint32_t ram_mask_ = 0; }; - friend DMADevice; + friend DMADeviceBase; + + template class DMADevice: public DMADeviceBase { + public: + 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) { + static_assert(id < num_addresses); + static_assert(shift == 0 || shift == 16); + addresses_[id] = (addresses_[id] & (0xffff'0000 >> shift)) | uint32_t(value << shift); + } + + protected: + std::array addresses_{}; + }; // MARK: - Interrupts. @@ -147,9 +162,6 @@ class Chipset { // Current raster position. int line_cycle_ = 0, y_ = 0; -// class Bitplanes: public DMADevice { -// -// }; // Parameters affecting bitplane collection and output. uint16_t display_window_start_[2] = {0, 0}; uint16_t display_window_stop_[2] = {0, 0}; @@ -157,15 +169,32 @@ class Chipset { // Ephemeral bitplane collection state. bool fetch_vertical_ = false, fetch_horizontal_ = false; + bool did_fetch_ = false; using BitplaneData = std::array; - BitplaneData next; + + class Bitplanes: public DMADevice<6> { + public: + using DMADevice::DMADevice; + + template bool advance(); + void do_end_of_line(); + + private: + bool is_high_res_ = false; + int collection_offset_ = 0; + int plane_count_ = 1; + + BitplaneData next; + } bitplanes_; + + void post_bitplanes(const BitplaneData &data); // MARK: - Copper. - class Copper: public DMADevice { + class Copper: public DMADevice<2> { public: - using DMADevice::DMADevice; + using DMADevice<2>::DMADevice; /// Offers a DMA slot to the Copper, specifying the current beam position. /// @@ -178,11 +207,6 @@ class Chipset { state_ = State::FetchFirstWord; } - /// 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) { - addresses_[id] = (addresses_[id] & (0xffff'0000 >> shift)) | uint32_t(value << shift); - } - /// Sets the Copper control word. void set_control(uint16_t c) { control_ = c; @@ -195,7 +219,6 @@ class Chipset { private: uint32_t address_ = 0; - uint32_t addresses_[2]{}; uint16_t control_ = 0; enum class State { @@ -220,28 +243,23 @@ class Chipset { // MARK: - Disk drives. - class DiskDMA: public DMADevice { + class DiskDMA: public DMADevice<1> { public: using DMADevice::DMADevice; - template void set_address(uint16_t value) { - address_ = (address_ & (0xffff'0000 >> shift)) | uint32_t(value << shift); - } - void set_length(uint16_t value) { dma_enable_ = value & 0x8000; write_ = value & 0x4000; length_ = value & 0x3fff; if(dma_enable_) { - printf("Not yet implemented: disk DMA [%s of %d to %06x]\n", write_ ? "write" : "read", length_, address_); + printf("Not yet implemented: disk DMA [%s of %d to %06x]\n", write_ ? "write" : "read", length_, addresses_[0]); } } bool advance(); private: - uint32_t address_; uint16_t length_; bool dma_enable_ = false; bool write_ = false; diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal.xcscheme b/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal.xcscheme index 85c8c1e2c..eb9346987 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal.xcscheme +++ b/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal.xcscheme @@ -65,6 +65,7 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + enableAddressSanitizer = "YES" enableASanStackUseAfterReturn = "YES" disableMainThreadChecker = "YES" launchStyle = "0"