diff --git a/Components/6850/6850.cpp b/Components/6850/6850.cpp index 9b08ca8eb..fed063e48 100644 --- a/Components/6850/6850.cpp +++ b/Components/6850/6850.cpp @@ -21,26 +21,11 @@ ACIA::ACIA(HalfCycles transmit_clock_rate, HalfCycles receive_clock_rate) : uint8_t ACIA::read(int address) { if(address&1) { - clear_interrupt_cause(ReceiveNeedsRead); received_data_ |= NoValueMask; + update_interrupt_line(); return uint8_t(received_data_); } else { - return - ((received_data_ & NoValueMask) ? 0x00 : 0x01) | - ((next_transmission_ == NoValueMask) ? 0x02 : 0x00) | - (data_carrier_detect.read() ? 0x04 : 0x00) | - (clear_to_send.read() ? 0x08 : 0x00) | - (interrupt_causes_ ? 0x80 : 0x00) - ; - - // b0: receive data full. - // b1: transmit data empty. - // b2: DCD. - // b3: CTS. - // b4: framing error (i.e. no first stop bit where expected). - // b5: receiver overran. - // b6: parity error. - // b7: IRQ state. + return get_status(); } } @@ -48,7 +33,7 @@ void ACIA::write(int address, uint8_t value) { if(address&1) { next_transmission_ = value; consider_transmission(); - clear_interrupt_cause(TransmitNeedsWrite); + update_interrupt_line(); } else { if((value&3) == 3) { transmit.reset_writing(); @@ -85,6 +70,8 @@ void ACIA::write(int address, uint8_t value) { } receive.set_read_delegate(this, Storage::Time(divider_ * 2, int(receive_clock_rate_.as_integral()))); receive_interrupt_enabled_ = value & 0x80; + + update_interrupt_line(); } } update_clocking_observer(); @@ -132,7 +119,7 @@ ClockingHint::Preference ACIA::preferred_clocking() { } bool ACIA::get_interrupt_line() const { - return interrupt_causes_; + return interrupt_line_; } int ACIA::expected_bits() { @@ -157,7 +144,7 @@ bool ACIA::serial_line_did_produce_bit(Serial::Line *line, int bit) { if(bits_received_ >= bit_target) { bits_received_ = 0; received_data_ = uint8_t(bits_incoming_ >> (12 - bit_target)); - if(receive_interrupt_enabled_) add_interrupt_cause(ReceiveNeedsRead); + update_interrupt_line(); update_clocking_observer(); return false; } @@ -173,16 +160,51 @@ 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; - if(interrupt_delegate_ && is_changing_state) +void ACIA::update_interrupt_line() { + const bool old_line = interrupt_line_; + + /* + "Bit 7 of the control register is the rie bit. When the rie bit is high, the rdrf, ndcd, + and ovr bits will assert the nirq output. When the rie bit is low, nirq generation is disabled." + + rie = read interrupt enable + rdrf = receive data register full (status word bit 0) + ndcd = data carrier detect (status word bit 2) + over = receiver overrun (status word bit 5) + + "Bit 1 of the status register is the tdre bit. When high, the tdre bit indicates that data has been + transferred from the transmitter data register to the output shift register. At this point, the a6850 + is ready to accept a new transmit data byte. However, if the ncts signal is high, the tdre bit remains + low regardless of the status of the transmitter data register. Also, if transmit interrupt is enabled, + the nirq output is asserted." + + tdre = transmitter data register empty + ncts = clear to send + */ + const auto status = get_status(); + interrupt_line_ = + (receive_interrupt_enabled_ && (status & 0x25)) || + (transmit_interrupt_enabled_ && (status & 0x02)); + + if(interrupt_delegate_ && old_line != interrupt_line_) 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); +uint8_t ACIA::get_status() { + return + ((received_data_ & NoValueMask) ? 0x00 : 0x01) | + ((next_transmission_ == NoValueMask) ? 0x02 : 0x00) | +// (data_carrier_detect.read() ? 0x04 : 0x00) | +// (clear_to_send.read() ? 0x08 : 0x00) | + (interrupt_line_ ? 0x80 : 0x00) + ; + + // b0: receive data full. + // b1: transmit data empty. + // b2: DCD. + // b3: CTS. + // b4: framing error (i.e. no first stop bit where expected). + // b5: receiver overran. + // b6: parity error. + // b7: IRQ state. } diff --git a/Components/6850/6850.hpp b/Components/6850/6850.hpp index 1cda3e89e..e6c3eb92e 100644 --- a/Components/6850/6850.hpp +++ b/Components/6850/6850.hpp @@ -65,7 +65,7 @@ class ACIA: public ClockingHint::Source, private Serial::Line::ReadDelegate { } else { transmit.advance_writer(transmission_cycles); update_clocking_observer(); - if(transmit_interrupt_enabled_) add_interrupt_cause(TransmitNeedsWrite); + update_interrupt_line(); } } else { transmit.advance_writer(transmission_cycles); @@ -118,15 +118,10 @@ class ACIA: public ClockingHint::Source, private Serial::Line::ReadDelegate { 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); + bool interrupt_line_ = false; + void update_interrupt_line(); InterruptDelegate *interrupt_delegate_ = nullptr; + uint8_t get_status(); }; }