1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-11-29 12:50:28 +00:00

Attempts properly to maintain interrupt flag; adds delegate.

This commit is contained in:
Thomas Harte 2019-10-21 22:40:38 -04:00
parent ed9a5b0430
commit 7cb82fccc0
2 changed files with 40 additions and 18 deletions

View File

@ -8,10 +8,6 @@
#include "6850.hpp" #include "6850.hpp"
#define LOG_PREFIX "[6850] "
#define NDEBUG
#include "../../Outputs/Log.hpp"
using namespace Motorola::ACIA; using namespace Motorola::ACIA;
const HalfCycles ACIA::SameAsTransmit; const HalfCycles ACIA::SameAsTransmit;
@ -25,17 +21,16 @@ ACIA::ACIA(HalfCycles transmit_clock_rate, HalfCycles receive_clock_rate) :
uint8_t ACIA::read(int address) { uint8_t ACIA::read(int address) {
if(address&1) { if(address&1) {
LOG("Read from receive register"); clear_interrupt_cause(ReceiveNeedsRead);
interrupt_request_ = false;
received_data_ |= NoValueMask; received_data_ |= NoValueMask;
} else { } else {
LOG("Read status"); clear_interrupt_cause(StatusNeedsRead);
return return
((received_data_ & NoValueMask) ? 0x00 : 0x01) | ((received_data_ & NoValueMask) ? 0x00 : 0x01) |
((next_transmission_ == NoValueMask) ? 0x02 : 0x00) | ((next_transmission_ == NoValueMask) ? 0x02 : 0x00) |
(data_carrier_detect.read() ? 0x04 : 0x00) | (data_carrier_detect.read() ? 0x04 : 0x00) |
(clear_to_send.read() ? 0x08 : 0x00) | (clear_to_send.read() ? 0x08 : 0x00) |
(interrupt_request_ ? 0x80 : 0x00) (interrupt_causes_ ? 0x80 : 0x00)
; ;
// b0: receive data full. // b0: receive data full.
@ -54,7 +49,7 @@ void ACIA::write(int address, uint8_t value) {
if(address&1) { if(address&1) {
next_transmission_ = value; next_transmission_ = value;
consider_transmission(); consider_transmission();
interrupt_request_ = false; clear_interrupt_cause(TransmitNeedsWrite);
} else { } else {
if((value&3) == 3) { if((value&3) == 3) {
transmit.reset_writing(); transmit.reset_writing();
@ -114,7 +109,7 @@ void ACIA::run_for(HalfCycles length) {
} else { } else {
transmit.advance_writer(transmit_advance); transmit.advance_writer(transmit_advance);
update_clocking_observer(); update_clocking_observer();
interrupt_request_ |= transmit_interrupt_enabled_; if(transmit_interrupt_enabled_) add_interrupt_cause(TransmitNeedsWrite);
} }
} else { } else {
transmit.advance_writer(transmit_advance); transmit.advance_writer(transmit_advance);
@ -143,11 +138,7 @@ void ACIA::consider_transmission() {
// Output all that. // Output all that.
const int total_bits = expected_bits(); const int total_bits = expected_bits();
if(!next_transmission_) {
printf("");
}
transmit.write(divider_ * 2, total_bits, transmission); transmit.write(divider_ * 2, total_bits, transmission);
printf("Transmitted %02x [%03x]\n", next_transmission_, transmission);
// Mark the transmit register as empty again. // Mark the transmit register as empty again.
next_transmission_ = NoValueMask; next_transmission_ = NoValueMask;
@ -168,7 +159,7 @@ ClockingHint::Preference ACIA::preferred_clocking() {
} }
bool ACIA::get_interrupt_line() const { bool ACIA::get_interrupt_line() const {
return interrupt_request_; return interrupt_causes_;
} }
int ACIA::expected_bits() { int ACIA::expected_bits() {
@ -193,7 +184,7 @@ bool ACIA::serial_line_did_produce_bit(Serial::Line *line, int bit) {
if(bits_received_ >= bit_target) { if(bits_received_ >= bit_target) {
bits_received_ = 0; bits_received_ = 0;
received_data_ = uint8_t(bits_incoming_ >> (12 - bit_target)); received_data_ = uint8_t(bits_incoming_ >> (12 - bit_target));
interrupt_request_ |= receive_interrupt_enabled_; if(receive_interrupt_enabled_) add_interrupt_cause(ReceiveNeedsRead);
update_clocking_observer(); update_clocking_observer();
return false; return false;
} }
@ -204,3 +195,21 @@ bool ACIA::serial_line_did_produce_bit(Serial::Line *line, int bit) {
if(bits_received_ == 1) update_clocking_observer(); if(bits_received_ == 1) update_clocking_observer();
return true; return true;
} }
void ACIA::set_interrupt_delegate(InterruptDelegate *delegate) {
interrupt_delegate_ = delegate;
}
void ACIA::add_interrupt_cause(int cause) {
const bool is_changing_state = !interrupt_causes_;
interrupt_causes_ |= cause | StatusNeedsRead;
if(interrupt_delegate_ && is_changing_state)
interrupt_delegate_->acia6850_did_change_interrupt_status(this);
}
void ACIA::clear_interrupt_cause(int cause) {
const bool was_set = interrupt_causes_;
interrupt_causes_ &= ~cause;
if(interrupt_delegate_ && was_set && !interrupt_causes_)
interrupt_delegate_->acia6850_did_change_interrupt_status(this);
}

View File

@ -65,6 +65,11 @@ class ACIA: public ClockingHint::Source, private Serial::Line::ReadDelegate {
// ClockingHint::Source. // ClockingHint::Source.
ClockingHint::Preference preferred_clocking() final; ClockingHint::Preference preferred_clocking() final;
struct InterruptDelegate {
virtual void acia6850_did_change_interrupt_status(ACIA *acia) = 0;
};
void set_interrupt_delegate(InterruptDelegate *delegate);
private: private:
int divider_ = 1; int divider_ = 1;
enum class Parity { enum class Parity {
@ -86,12 +91,20 @@ class ACIA: public ClockingHint::Source, private Serial::Line::ReadDelegate {
bool receive_interrupt_enabled_ = false; bool receive_interrupt_enabled_ = false;
bool transmit_interrupt_enabled_ = false; bool transmit_interrupt_enabled_ = false;
bool interrupt_request_ = false;
HalfCycles transmit_clock_rate_; HalfCycles transmit_clock_rate_;
HalfCycles receive_clock_rate_; HalfCycles receive_clock_rate_;
bool serial_line_did_produce_bit(Serial::Line *line, int bit) final; bool serial_line_did_produce_bit(Serial::Line *line, int bit) final;
enum InterruptCause: int {
TransmitNeedsWrite = 1 << 0,
ReceiveNeedsRead = 1 << 1,
StatusNeedsRead = 1 << 2
};
int interrupt_causes_ = 0;
void add_interrupt_cause(int cause);
void clear_interrupt_cause(int cause);
InterruptDelegate *interrupt_delegate_ = nullptr;
}; };
} }