From 48b0d8c3293d050ff7b0966bd32cebe12bdbb223 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 3 Nov 2019 21:11:25 -0500 Subject: [PATCH] Adds bus req/ack to the DMA controller; hacks support into the ST. --- Machines/AtariST/AtariST.cpp | 15 ++++-- Machines/AtariST/DMAController.cpp | 81 ++++++++++++++++++++++++++---- Machines/AtariST/DMAController.hpp | 23 +++++++-- 3 files changed, 99 insertions(+), 20 deletions(-) diff --git a/Machines/AtariST/AtariST.cpp b/Machines/AtariST/AtariST.cpp index 7997100ba..9127f1b21 100644 --- a/Machines/AtariST/AtariST.cpp +++ b/Machines/AtariST/AtariST.cpp @@ -46,7 +46,7 @@ class ConcreteMachine: public ClockingHint::Observer, public Motorola::ACIA::ACIA::InterruptDelegate, public Motorola::MFP68901::MFP68901::InterruptDelegate, - public DMAController::InterruptDelegate, + public DMAController::Delegate, public MouseMachine::Machine, public KeyboardMachine::MappedMachine, public MediaTarget::Machine, @@ -101,7 +101,7 @@ class ConcreteMachine: dma_->set_clocking_hint_observer(this); mfp_->set_interrupt_delegate(this); - dma_->set_interrupt_delegate(this); + dma_->set_delegate(this); ay_.set_port_handler(this); set_gpip_input(); @@ -436,8 +436,15 @@ class ConcreteMachine: void acia6850_did_change_interrupt_status(Motorola::ACIA::ACIA *) final { set_gpip_input(); } - void dma_controller_did_change_interrupt_status(DMAController *) final { + void dma_controller_did_change_output(DMAController *) final { set_gpip_input(); + + // Filty hack, here! Should: set the 68000's bus request line. But until + // that's implemented, just offers magical zero-cost DMA insertion and + // extrication. + if(dma_->get_bus_request_line()) { + dma_->bus_grant(ram_.data(), ram_.size()); + } } void set_gpip_input() { /* @@ -512,7 +519,7 @@ class ConcreteMachine: b1: select floppy drive 0 b0: "page choice signal for double-sided floppy drive" */ - dma_->set_floppy_drive_selection(!(value & 2), !(value & 4), value & 1); + dma_->set_floppy_drive_selection(!(value & 2), !(value & 4), !(value & 1)); } } diff --git a/Machines/AtariST/DMAController.cpp b/Machines/AtariST/DMAController.cpp index 014b740a2..c22f75e89 100644 --- a/Machines/AtariST/DMAController.cpp +++ b/Machines/AtariST/DMAController.cpp @@ -126,39 +126,98 @@ void DMAController::wd1770_did_change_output(WD::WD1770 *) { // Check for a change in interrupt state. const bool old_interrupt_line = interrupt_line_; interrupt_line_ = fdc_.get_interrupt_request_line(); - if(interrupt_delegate_ && interrupt_line_ != old_interrupt_line) { - interrupt_delegate_->dma_controller_did_change_interrupt_status(this); + if(delegate_ && interrupt_line_ != old_interrupt_line) { + delegate_->dma_controller_did_change_output(this); } // Check for a change in DRQ state, if it's the FDC that is currently being watched. - if(fdc_.get_data_request_line()) { - if(!(control_ & Control::DRQSource)) return; + if(byte_count_ && fdc_.get_data_request_line() && (control_ & Control::DRQSource)) { + --byte_count_; + if(control_ & Control::Direction) { // TODO: DMA is supposed to be helping with a write. } else { // DMA is enabling a read. // Read from the data register into the active buffer. - buffer_[active_buffer_][bytes_received_] = fdc_.get_register(3); - ++bytes_received_; + if(bytes_received_ < 16) { + buffer_[active_buffer_].contents[bytes_received_] = fdc_.get_register(3); + ++bytes_received_; + } if(bytes_received_ == 16) { - // TODO: BusReq and eventual deposit into RAM? + // Mark buffer as full. + buffer_[active_buffer_].is_full = true; - active_buffer_ ^= 1; - bytes_received_ = 0; + // Move to the next if it is empty; if it isn't, note a DMA error. + const auto next_buffer = active_buffer_ ^ 1; + error_ |= buffer_[next_buffer].is_full; + if(!buffer_[next_buffer].is_full) { + bytes_received_ = 0; + active_buffer_ = next_buffer; + } + + // Set bus request. + if(!bus_request_line_) { + bus_request_line_ = true; + if(delegate_) delegate_->dma_controller_did_change_output(this); + } } } } } -void DMAController::set_interrupt_delegate(InterruptDelegate *delegate) { - interrupt_delegate_ = delegate; +int DMAController::bus_grant(uint16_t *ram, size_t size) { + // Being granted the bus negates the request. + bus_request_line_ = false; + if(delegate_) delegate_->dma_controller_did_change_output(this); + + if(control_ & Control::Direction) { + // TODO: writes. + return 0; + } else { + // Check that the older buffer is full; stop if not. + if(!buffer_[active_buffer_ ^ 1].is_full) return 0; + + for(int c = 0; c < 8; ++c) { + ram[address_ >> 1] = uint16_t( + (buffer_[active_buffer_ ^ 1].contents[(c << 1) + 0] << 8) | + (buffer_[active_buffer_ ^ 1].contents[(c << 1) + 1] << 0) + ); + address_ += 2; + } + buffer_[active_buffer_ ^ 1].is_full = false; + + // Check that the newer buffer is full; stop if not. + if(!buffer_[active_buffer_ ].is_full) return 8; + + for(int c = 0; c < 8; ++c) { + ram[address_ >> 1] = uint16_t( + (buffer_[active_buffer_].contents[(c << 1) + 0] << 8) | + (buffer_[active_buffer_].contents[(c << 1) + 1] << 0) + ); + address_ += 2; + } + buffer_[active_buffer_].is_full = false; + + // Both buffers were full, so unblock reading. + bytes_received_ = 0; + + return 16; + } +} + +void DMAController::set_delegate(Delegate *delegate) { + delegate_ = delegate; } bool DMAController::get_interrupt_line() { return interrupt_line_; } +bool DMAController::get_bus_request_line() { + return bus_request_line_; +} + void DMAController::set_component_prefers_clocking(ClockingHint::Source *, ClockingHint::Preference) { update_clocking_observer(); } diff --git a/Machines/AtariST/DMAController.hpp b/Machines/AtariST/DMAController.hpp index b8b900519..8df40bb28 100644 --- a/Machines/AtariST/DMAController.hpp +++ b/Machines/AtariST/DMAController.hpp @@ -28,14 +28,23 @@ class DMAController: public WD::WD1770::Delegate, public ClockingHint::Source, p void run_for(HalfCycles duration); bool get_interrupt_line(); + bool get_bus_request_line(); + + /*! + Indicates that the DMA controller has been granted bus access to the block of memory at @c ram, which + is of size @c size. + + @returns The number of words read or written. + */ + int bus_grant(uint16_t *ram, size_t size); void set_floppy_drive_selection(bool drive1, bool drive2, bool side2); void set_floppy_disk(std::shared_ptr disk, size_t drive); - struct InterruptDelegate { - virtual void dma_controller_did_change_interrupt_status(DMAController *) = 0; + struct Delegate { + virtual void dma_controller_did_change_output(DMAController *) = 0; }; - void set_interrupt_delegate(InterruptDelegate *delegate); + void set_delegate(Delegate *delegate); // ClockingHint::Source. ClockingHint::Preference preferred_clocking() final; @@ -74,13 +83,17 @@ class DMAController: public WD::WD1770::Delegate, public ClockingHint::Source, p uint16_t control_ = 0; - InterruptDelegate *interrupt_delegate_ = nullptr; + Delegate *delegate_ = nullptr; bool interrupt_line_ = false; + bool bus_request_line_ = false; void set_component_prefers_clocking(ClockingHint::Source *, ClockingHint::Preference) final; // MARK: - DMA State. - uint8_t buffer_[2][16]; + struct Buffer { + uint8_t contents[16]; + bool is_full = false; + } buffer_[2]; int active_buffer_ = 0; int bytes_received_ = 0; bool error_ = false;