mirror of
https://github.com/TomHarte/CLK.git
synced 2024-09-08 18:55:14 +00:00
Adds bus req/ack to the DMA controller; hacks support into the ST.
This commit is contained in:
parent
07582cee4a
commit
48b0d8c329
@ -46,7 +46,7 @@ class ConcreteMachine:
|
|||||||
public ClockingHint::Observer,
|
public ClockingHint::Observer,
|
||||||
public Motorola::ACIA::ACIA::InterruptDelegate,
|
public Motorola::ACIA::ACIA::InterruptDelegate,
|
||||||
public Motorola::MFP68901::MFP68901::InterruptDelegate,
|
public Motorola::MFP68901::MFP68901::InterruptDelegate,
|
||||||
public DMAController::InterruptDelegate,
|
public DMAController::Delegate,
|
||||||
public MouseMachine::Machine,
|
public MouseMachine::Machine,
|
||||||
public KeyboardMachine::MappedMachine,
|
public KeyboardMachine::MappedMachine,
|
||||||
public MediaTarget::Machine,
|
public MediaTarget::Machine,
|
||||||
@ -101,7 +101,7 @@ class ConcreteMachine:
|
|||||||
dma_->set_clocking_hint_observer(this);
|
dma_->set_clocking_hint_observer(this);
|
||||||
|
|
||||||
mfp_->set_interrupt_delegate(this);
|
mfp_->set_interrupt_delegate(this);
|
||||||
dma_->set_interrupt_delegate(this);
|
dma_->set_delegate(this);
|
||||||
ay_.set_port_handler(this);
|
ay_.set_port_handler(this);
|
||||||
|
|
||||||
set_gpip_input();
|
set_gpip_input();
|
||||||
@ -436,8 +436,15 @@ class ConcreteMachine:
|
|||||||
void acia6850_did_change_interrupt_status(Motorola::ACIA::ACIA *) final {
|
void acia6850_did_change_interrupt_status(Motorola::ACIA::ACIA *) final {
|
||||||
set_gpip_input();
|
set_gpip_input();
|
||||||
}
|
}
|
||||||
void dma_controller_did_change_interrupt_status(DMAController *) final {
|
void dma_controller_did_change_output(DMAController *) final {
|
||||||
set_gpip_input();
|
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() {
|
void set_gpip_input() {
|
||||||
/*
|
/*
|
||||||
@ -512,7 +519,7 @@ class ConcreteMachine:
|
|||||||
b1: select floppy drive 0
|
b1: select floppy drive 0
|
||||||
b0: "page choice signal for double-sided floppy drive"
|
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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,39 +126,98 @@ void DMAController::wd1770_did_change_output(WD::WD1770 *) {
|
|||||||
// Check for a change in interrupt state.
|
// Check for a change in interrupt state.
|
||||||
const bool old_interrupt_line = interrupt_line_;
|
const bool old_interrupt_line = interrupt_line_;
|
||||||
interrupt_line_ = fdc_.get_interrupt_request_line();
|
interrupt_line_ = fdc_.get_interrupt_request_line();
|
||||||
if(interrupt_delegate_ && interrupt_line_ != old_interrupt_line) {
|
if(delegate_ && interrupt_line_ != old_interrupt_line) {
|
||||||
interrupt_delegate_->dma_controller_did_change_interrupt_status(this);
|
delegate_->dma_controller_did_change_output(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for a change in DRQ state, if it's the FDC that is currently being watched.
|
// Check for a change in DRQ state, if it's the FDC that is currently being watched.
|
||||||
if(fdc_.get_data_request_line()) {
|
if(byte_count_ && fdc_.get_data_request_line() && (control_ & Control::DRQSource)) {
|
||||||
if(!(control_ & Control::DRQSource)) return;
|
--byte_count_;
|
||||||
|
|
||||||
if(control_ & Control::Direction) {
|
if(control_ & Control::Direction) {
|
||||||
// TODO: DMA is supposed to be helping with a write.
|
// TODO: DMA is supposed to be helping with a write.
|
||||||
} else {
|
} else {
|
||||||
// DMA is enabling a read.
|
// DMA is enabling a read.
|
||||||
|
|
||||||
// Read from the data register into the active buffer.
|
// Read from the data register into the active buffer.
|
||||||
buffer_[active_buffer_][bytes_received_] = fdc_.get_register(3);
|
if(bytes_received_ < 16) {
|
||||||
|
buffer_[active_buffer_].contents[bytes_received_] = fdc_.get_register(3);
|
||||||
++bytes_received_;
|
++bytes_received_;
|
||||||
|
}
|
||||||
if(bytes_received_ == 16) {
|
if(bytes_received_ == 16) {
|
||||||
// TODO: BusReq and eventual deposit into RAM?
|
// Mark buffer as full.
|
||||||
|
buffer_[active_buffer_].is_full = true;
|
||||||
|
|
||||||
active_buffer_ ^= 1;
|
// 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;
|
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) {
|
int DMAController::bus_grant(uint16_t *ram, size_t size) {
|
||||||
interrupt_delegate_ = delegate;
|
// 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() {
|
bool DMAController::get_interrupt_line() {
|
||||||
return 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) {
|
void DMAController::set_component_prefers_clocking(ClockingHint::Source *, ClockingHint::Preference) {
|
||||||
update_clocking_observer();
|
update_clocking_observer();
|
||||||
}
|
}
|
||||||
|
@ -28,14 +28,23 @@ class DMAController: public WD::WD1770::Delegate, public ClockingHint::Source, p
|
|||||||
void run_for(HalfCycles duration);
|
void run_for(HalfCycles duration);
|
||||||
|
|
||||||
bool get_interrupt_line();
|
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_drive_selection(bool drive1, bool drive2, bool side2);
|
||||||
void set_floppy_disk(std::shared_ptr<Storage::Disk::Disk> disk, size_t drive);
|
void set_floppy_disk(std::shared_ptr<Storage::Disk::Disk> disk, size_t drive);
|
||||||
|
|
||||||
struct InterruptDelegate {
|
struct Delegate {
|
||||||
virtual void dma_controller_did_change_interrupt_status(DMAController *) = 0;
|
virtual void dma_controller_did_change_output(DMAController *) = 0;
|
||||||
};
|
};
|
||||||
void set_interrupt_delegate(InterruptDelegate *delegate);
|
void set_delegate(Delegate *delegate);
|
||||||
|
|
||||||
// ClockingHint::Source.
|
// ClockingHint::Source.
|
||||||
ClockingHint::Preference preferred_clocking() final;
|
ClockingHint::Preference preferred_clocking() final;
|
||||||
@ -74,13 +83,17 @@ class DMAController: public WD::WD1770::Delegate, public ClockingHint::Source, p
|
|||||||
|
|
||||||
uint16_t control_ = 0;
|
uint16_t control_ = 0;
|
||||||
|
|
||||||
InterruptDelegate *interrupt_delegate_ = nullptr;
|
Delegate *delegate_ = nullptr;
|
||||||
bool interrupt_line_ = false;
|
bool interrupt_line_ = false;
|
||||||
|
bool bus_request_line_ = false;
|
||||||
|
|
||||||
void set_component_prefers_clocking(ClockingHint::Source *, ClockingHint::Preference) final;
|
void set_component_prefers_clocking(ClockingHint::Source *, ClockingHint::Preference) final;
|
||||||
|
|
||||||
// MARK: - DMA State.
|
// 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 active_buffer_ = 0;
|
||||||
int bytes_received_ = 0;
|
int bytes_received_ = 0;
|
||||||
bool error_ = false;
|
bool error_ = false;
|
||||||
|
Loading…
Reference in New Issue
Block a user