From 45892f358471ecbd610b7c4f3dc402cab70ff2f0 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 6 Aug 2022 09:51:20 -0400 Subject: [PATCH] Add optional transaction records to the Blitter. --- Machines/Amiga/Blitter.cpp | 80 ++++++++++++++++++++++++++++++++------ Machines/Amiga/Blitter.hpp | 26 ++++++++++++- Machines/Amiga/Chipset.hpp | 2 +- 3 files changed, 94 insertions(+), 14 deletions(-) diff --git a/Machines/Amiga/Blitter.cpp b/Machines/Amiga/Blitter.cpp index 74df53d15..6290bfa11 100644 --- a/Machines/Amiga/Blitter.cpp +++ b/Machines/Amiga/Blitter.cpp @@ -113,7 +113,8 @@ constexpr uint32_t fill_values[] = { } -void Blitter::set_control(int index, uint16_t value) { +template +void Blitter::set_control(int index, uint16_t value) { if(index) { line_mode_ = (value & 0x0001); one_dot_ = value & 0x0002; @@ -132,17 +133,20 @@ void Blitter::set_control(int index, uint16_t value) { LOG("Set control " << index << " to " << PADHEX(4) << value); } -void Blitter::set_first_word_mask(uint16_t value) { +template +void Blitter::set_first_word_mask(uint16_t value) { LOG("Set first word mask: " << PADHEX(4) << value); a_mask_[0] = value; } -void Blitter::set_last_word_mask(uint16_t value) { +template +void Blitter::set_last_word_mask(uint16_t value) { LOG("Set last word mask: " << PADHEX(4) << value); a_mask_[1] = value; } -void Blitter::set_size(uint16_t value) { +template +void Blitter::set_size(uint16_t value) { // width_ = (width_ & ~0x3f) | (value & 0x3f); // height_ = (height_ & ~0x3ff) | (value >> 6); width_ = value & 0x3f; @@ -155,21 +159,25 @@ void Blitter::set_size(uint16_t value) { // blitter that it should treat itself as about to start a new line. } -void Blitter::set_minterms(uint16_t value) { +template +void Blitter::set_minterms(uint16_t value) { LOG("Set minterms " << PADHEX(4) << value); minterms_ = value & 0xff; } -//void Blitter::set_vertical_size([[maybe_unused]] uint16_t value) { +//template +//void Blitter::set_vertical_size([[maybe_unused]] uint16_t value) { // LOG("Set vertical size " << PADHEX(4) << value); // // TODO. This is ECS only, I think. Ditto set_horizontal_size. //} // -//void Blitter::set_horizontal_size([[maybe_unused]] uint16_t value) { +//template +//void Blitter::set_horizontal_size([[maybe_unused]] uint16_t value) { // LOG("Set horizontal size " << PADHEX(4) << value); //} -void Blitter::set_data(int channel, uint16_t value) { +template +void Blitter::set_data(int channel, uint16_t value) { LOG("Set data " << channel << " to " << PADHEX(4) << value); // Ugh, backed myself into a corner. TODO: clean. @@ -181,7 +189,8 @@ void Blitter::set_data(int channel, uint16_t value) { } } -uint16_t Blitter::get_status() { +template +uint16_t Blitter::get_status() { const uint16_t result = (not_zero_flag_ ? 0x0000 : 0x2000) | (height_ ? 0x4000 : 0x0000); LOG("Returned status of " << result); @@ -216,14 +225,16 @@ uint16_t Blitter::get_status() { // // Table 6-2: Typical Blitter Cycle Sequence -void Blitter::add_modulos() { +template +void Blitter::add_modulos() { pointer_[0] += modulos_[0] * sequencer_.channel_enabled<0>()* direction_; pointer_[1] += modulos_[1] * sequencer_.channel_enabled<1>() * direction_; pointer_[2] += modulos_[2] * sequencer_.channel_enabled<2>() * direction_; pointer_[3] += modulos_[3] * sequencer_.channel_enabled<3>() * direction_; } -bool Blitter::advance_dma() { +template +bool Blitter::advance_dma() { if(!height_) return false; if(line_mode_) { @@ -289,11 +300,18 @@ bool Blitter::advance_dma() { // TODO: patterned lines. Unclear what to do with the bit that comes out of b. // Probably extend it to a full word? c_data_ = ram_[pointer_[3] & ram_mask_]; + if constexpr (record_bus) { + transactions_.emplace_back(Transaction::Type::ReadC, pointer_[3], c_data_); + } + const uint16_t output = apply_minterm(a_data_ >> shifts_[0], b_data_, c_data_, minterms_); ram_[pointer_[3] & ram_mask_] = output; not_zero_flag_ |= output; draw_ &= !one_dot_; + if constexpr (record_bus) { + transactions_.emplace_back(Transaction::Type::WriteFromPipeline, pointer_[3], output); + } } constexpr int LEFT = 1 << 0; @@ -383,14 +401,26 @@ bool Blitter::advance_dma() { switch(next.first) { case Channel::A: a_data_ = ram_[pointer_[0] & ram_mask_]; + + if constexpr (record_bus) { + transactions_.emplace_back(Transaction::Type::ReadA, pointer_[0], a_data_); + } pointer_[0] += direction_; return true; case Channel::B: b_data_ = ram_[pointer_[1] & ram_mask_]; + + if constexpr (record_bus) { + transactions_.emplace_back(Transaction::Type::ReadB, pointer_[1], b_data_); + } pointer_[1] += direction_; return true; case Channel::C: c_data_ = ram_[pointer_[2] & ram_mask_]; + + if constexpr (record_bus) { + transactions_.emplace_back(Transaction::Type::ReadC, pointer_[2], c_data_); + } pointer_[2] += direction_; return true; case Channel::FlushPipeline: @@ -400,11 +430,20 @@ bool Blitter::advance_dma() { busy_ = false; if(write_phase_ == WritePhase::Full) { + if constexpr (record_bus) { + transactions_.emplace_back(Transaction::Type::WriteFromPipeline, write_address_, write_value_); + } ram_[write_address_ & ram_mask_] = write_value_; + write_phase_ = WritePhase::Starting; } return true; - case Channel::None: return false; + case Channel::None: + if constexpr (record_bus) { + transactions_.emplace_back(Transaction::Type::SkippedSlot); + } + return false; + case Channel::Write: break; } @@ -458,6 +497,9 @@ bool Blitter::advance_dma() { switch(write_phase_) { case WritePhase::Full: + if constexpr (record_bus) { + transactions_.emplace_back(Transaction::Type::WriteFromPipeline, write_address_, write_value_); + } ram_[write_address_ & ram_mask_] = write_value_; [[fallthrough]]; @@ -465,6 +507,10 @@ bool Blitter::advance_dma() { write_phase_ = WritePhase::Full; write_address_ = pointer_[3]; write_value_ = output; + + if constexpr (record_bus) { + transactions_.emplace_back(Transaction::Type::AddToPipeline, write_address_, write_value_); + } pointer_[3] += direction_; return true; @@ -477,3 +523,13 @@ bool Blitter::advance_dma() { return true; } + +template +std::vector::Transaction> Blitter::get_and_reset_transactions() { + std::vector result; + std::swap(result, transactions_); + return result; +} + +template class Amiga::Blitter; +template class Amiga::Blitter; diff --git a/Machines/Amiga/Blitter.hpp b/Machines/Amiga/Blitter.hpp index 389c7b7dd..72b7a2012 100644 --- a/Machines/Amiga/Blitter.hpp +++ b/Machines/Amiga/Blitter.hpp @@ -11,6 +11,7 @@ #include #include +#include #include "../../ClockReceiver/ClockReceiver.hpp" #include "DMADevice.hpp" @@ -166,7 +167,11 @@ class BlitterSequencer { Phase next_phase_ = Phase::Complete; }; -class Blitter: public DMADevice<4, 4> { +/*! + If @c record_bus is @c true then all bus interactions will be recorded + and can subsequently be retrieved. This is included for testing purposes. +*/ +template class Blitter: public DMADevice<4, 4> { public: using DMADevice::DMADevice; @@ -188,6 +193,24 @@ class Blitter: public DMADevice<4, 4> { bool advance_dma(); + struct Transaction { + enum class Type { + SkippedSlot, + ReadA, + ReadB, + ReadC, + AddToPipeline, + WriteFromPipeline + } type; + + uint32_t address = 0; + uint16_t value = 0; + + Transaction(Type type) : type(type) {} + Transaction(Type type, uint32_t address, uint16_t value) : type(type), address(address), value(value) {} + }; + std::vector get_and_reset_transactions(); + private: int width_ = 0, height_ = 0; int shifts_[2]{}; @@ -221,6 +244,7 @@ class Blitter: public DMADevice<4, 4> { int loop_index_ = -1; void add_modulos(); + std::vector transactions_; }; } diff --git a/Machines/Amiga/Chipset.hpp b/Machines/Amiga/Chipset.hpp index 1e95e7209..9957f0328 100644 --- a/Machines/Amiga/Chipset.hpp +++ b/Machines/Amiga/Chipset.hpp @@ -139,7 +139,7 @@ class Chipset: private ClockingHint::Observer { // MARK: - DMA Control, Scheduler and Blitter. uint16_t dma_control_ = 0; - Blitter blitter_; + Blitter blitter_; // MARK: - Sprites and collision flags.