From dac40630fdc4b91ee83dda2915a8ceb3df14757c Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 27 Nov 2021 11:36:15 -0500 Subject: [PATCH] Adds support for the Blitter-busy flag to WAIT and SKIP. --- Machines/Amiga/Chipset.cpp | 2 +- Machines/Amiga/Copper.cpp | 76 ++++++++++++++++++++++++++++++++------ Machines/Amiga/Copper.hpp | 5 +-- 3 files changed, 68 insertions(+), 15 deletions(-) diff --git a/Machines/Amiga/Chipset.cpp b/Machines/Amiga/Chipset.cpp index 026423db4..b94144d71 100644 --- a/Machines/Amiga/Chipset.cpp +++ b/Machines/Amiga/Chipset.cpp @@ -434,7 +434,7 @@ template bool Chipset::perform_cycle() { // // The Blitter and CPU are dealt with outside of the odd/even test. if((dma_control_ & CopperFlag) == CopperFlag) { - if(copper_.advance_dma(uint16_t(((y_ & 0xff) << 8) | (cycle & 0xfe)))) { + if(copper_.advance_dma(uint16_t(((y_ & 0xff) << 8) | (cycle & 0xfe)), blitter_.get_status())) { return false; } } else { diff --git a/Machines/Amiga/Copper.cpp b/Machines/Amiga/Copper.cpp index 2f6f6844a..dc017b719 100644 --- a/Machines/Amiga/Copper.cpp +++ b/Machines/Amiga/Copper.cpp @@ -18,13 +18,69 @@ using namespace Amiga; -bool Copper::advance_dma(uint16_t position) { +namespace { + +bool satisfies_raster(uint16_t position, uint16_t blitter_status, uint16_t *instruction) { + const uint16_t mask = 0x8000 | (instruction[1] & 0x7ffe); + return + (position & mask) >= (instruction[0] & mask) + && (!(blitter_status & 0x4000) || (instruction[1] & 0x8000)); +} + +} + +// +// Quick notes on the Copper: +// +// There are three instructions: move, wait and skip. +// +// Move writes a value to one of the Chipset registers; it is encoded as: +// +// First word: +// b0: 0 +// b1–b8: register address +// b9+: unused ("should be set to 0") +// +// Second word: +// b0–b15: value to move. +// +// +// Wait waits until the raster gets to at least a certain position, and +// optionally until the Blitter has finished. It is encoded as: +// +// First word: +// b0: 1 +// b1–b7: horizontal beam position +// b8+: vertical beam position +// +// Second word: +// b0: 0 +// b1–b7: horizontal beam comparison mask +// b8–b14: vertical beam comparison mask +// b15: 1 => don't also wait for the Blitter to be finished; 0 => wait. +// +// +// Skip skips the next instruction if the raster has already reached a certain +// position, and optionally only if the Blitter has finished, and only if the +// next instruction is a move. +// +// First word: +// b0: 1 +// b1–b7: horizontal beam position +// b8+: vertical beam position +// +// Second word: +// b0: 1 +// b1–b7: horizontal beam comparison mask +// b8–b14: vertical beam comparison mask +// b15: 1 => don't also wait for the Blitter to be finished; 0 => wait. +// +bool Copper::advance_dma(uint16_t position, uint16_t blitter_status) { switch(state_) { default: return false; case State::Waiting: - // TODO: blitter-finished bit. - if((position & position_mask_) >= instruction_[0]) { + if(satisfies_raster(position, blitter_status, instruction_)) { LOG("Unblocked waiting for " << PADHEX(4) << instruction_[0] << " at " << position); state_ = State::FetchFirstWord; } @@ -37,15 +93,16 @@ bool Copper::advance_dma(uint16_t position) { break; case State::FetchSecondWord: { + // Get and reset the should-skip-next flag. const bool should_skip_move = skip_next_; skip_next_ = false; + // Read in the second instruction word. instruction_[1] = ram_[address_ & ram_mask_]; ++address_; + // Check for a MOVE. if(!(instruction_[0] & 1)) { - // A MOVE. - if(!should_skip_move) { // Stop if this move would be a privilege violation. instruction_[0] &= 0x1fe; @@ -70,21 +127,18 @@ bool Copper::advance_dma(uint16_t position) { break; } - // Prepare for a position comparison. - position_mask_ = 0x8001 | (instruction_[1] & 0x7ffe); - instruction_[0] &= position_mask_; + // Got to here => this is a WAIT or a SKIP. if(!(instruction_[1] & 1)) { // A WAIT. Just note that this is now waiting; the proper test - // will be applied from the next potential `advance` onwards. + // will be applied from the next potential `advance_dma` onwards. state_ = State::Waiting; break; } // Neither a WAIT nor a MOVE => a SKIP. - // TODO: blitter-finished bit. - skip_next_ = (position & position_mask_) >= instruction_[0]; + skip_next_ = satisfies_raster(position, blitter_status, instruction_); state_ = State::FetchFirstWord; } break; } diff --git a/Machines/Amiga/Copper.hpp b/Machines/Amiga/Copper.hpp index 473cd00cb..761d2a6f6 100644 --- a/Machines/Amiga/Copper.hpp +++ b/Machines/Amiga/Copper.hpp @@ -17,10 +17,10 @@ class Copper: public DMADevice<2> { public: using DMADevice<2>::DMADevice; - /// Offers a DMA slot to the Copper, specifying the current beam position. + /// Offers a DMA slot to the Copper, specifying the current beam position and Blitter status. /// /// @returns @c true if the slot was used; @c false otherwise. - bool advance_dma(uint16_t position); + bool advance_dma(uint16_t position, uint16_t blitter_status); /// Forces a reload of address @c id (i.e. 0 or 1) and restarts the Copper. template void reload() { @@ -47,7 +47,6 @@ class Copper: public DMADevice<2> { } state_ = State::Stopped; bool skip_next_ = false; uint16_t instruction_[2]{}; - uint16_t position_mask_ = 0xffff; }; }