mirror of
https://github.com/TomHarte/CLK.git
synced 2024-12-23 20:29:42 +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 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));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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<Storage::Disk::Disk> 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;
|
||||
|
Loading…
Reference in New Issue
Block a user