diff --git a/Machines/Sinclair/ZXSpectrum/Video.hpp b/Machines/Sinclair/ZXSpectrum/Video.hpp index e278997cd..8419b882c 100644 --- a/Machines/Sinclair/ZXSpectrum/Video.hpp +++ b/Machines/Sinclair/ZXSpectrum/Video.hpp @@ -167,6 +167,8 @@ template class Video { last_fetches_[1] = memory_[attribute_address_]; last_fetches_[2] = memory_[pixel_address_+1]; last_fetches_[3] = memory_[attribute_address_+1]; + set_last_contended_area_access(last_fetches_[3]); + pixel_address_ += 2; attribute_address_ += 2; @@ -315,21 +317,37 @@ template class Video { } /*! - @returns Whatever the ULA or gate array has fetched this cycle, or 0xff if it has fetched nothing. + @returns Whatever the ULA or gate array would expose via the floating bus, this cycle. */ - uint8_t get_current_fetch() const { + uint8_t get_floating_value() const { constexpr auto timings = get_timings(); const int line = time_into_frame_ / timings.cycles_per_line; if(line >= 192) return 0xff; const int time_into_line = time_into_frame_ % timings.cycles_per_line; if(time_into_line >= 256 || (time_into_line&8)) { - return 0xff; + return last_contended_access_; + } + + // The +2a and +3 always return the low bit as set. + if constexpr (timing == VideoTiming::Plus3) { + return last_fetches_[(time_into_line >> 1) & 3] | 1; } return last_fetches_[(time_into_line >> 1) & 3]; } + /*! + Relevant to the +2a and +3 only, sets the most recent value read from or + written to contended memory. This is what will be returned if the floating + bus is accessed when the gate array isn't currently reading. + */ + void set_last_contended_area_access([[maybe_unused]] uint8_t value) { + if constexpr (timing == VideoTiming::Plus3) { + last_contended_access_ = value | 1; + } + } + /*! Sets the current border colour. */ @@ -367,6 +385,7 @@ template class Video { bool is_alternate_line_ = false; uint8_t last_fetches_[4] = {0xff, 0xff, 0xff, 0xff}; + uint8_t last_contended_access_ = 0xff; #define RGB(r, g, b) (r << 4) | (g << 2) | b static constexpr uint8_t palette[] = { diff --git a/Machines/Sinclair/ZXSpectrum/ZXSpectrum.cpp b/Machines/Sinclair/ZXSpectrum/ZXSpectrum.cpp index 5aafebbb1..e16fa633d 100644 --- a/Machines/Sinclair/ZXSpectrum/ZXSpectrum.cpp +++ b/Machines/Sinclair/ZXSpectrum/ZXSpectrum.cpp @@ -223,6 +223,10 @@ template class ConcreteMachine: case PartialMachineCycle::Read: *cycle.value = read_pointers_[address >> 14][address]; + + if(is_contended_[address >> 14]) { + video_.last_valid()->set_last_contended_area_access(*cycle.value); + } break; case PartialMachineCycle::Write: @@ -230,7 +234,13 @@ template class ConcreteMachine: if(is_video_[address >> 14] && (address & 0x3fff) < 6912) { video_.flush(); } + write_pointers_[address >> 14][address] = *cycle.value; + + // Fill the floating bus buffer if this write is within the contended area. + if(is_contended_[address >> 14]) { + video_.last_valid()->set_last_contended_area_access(*cycle.value); + } break; case PartialMachineCycle::Output: @@ -331,8 +341,9 @@ template class ConcreteMachine: // Check for a floating bus read; these are particularly arcane // on the +2a/+3. See footnote to https://spectrumforeveryone.com/technical/memory-contention-floating-bus/ - if((address & 0xf003) == 0x0001) { - *cycle.value &= video_->get_current_fetch(); + // and, much more rigorously, http://sky.relative-path.com/zx/floating_bus.html + if(!disable_paging_ && (address & 0xf003) == 0x0001) { + *cycle.value &= video_->get_floating_value(); } if constexpr (model == Model::Plus3) {