1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-11-25 16:31:42 +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"
#define LOG_PREFIX "[6850] "
#define NDEBUG
#include "../../Outputs/Log.hpp"
using namespace Motorola::ACIA;
const HalfCycles ACIA::SameAsTransmit;
@ -25,17 +21,16 @@ ACIA::ACIA(HalfCycles transmit_clock_rate, HalfCycles receive_clock_rate) :
uint8_t ACIA::read(int address) {
if(address&1) {
LOG("Read from receive register");
interrupt_request_ = false;
clear_interrupt_cause(ReceiveNeedsRead);
received_data_ |= NoValueMask;
} else {
LOG("Read status");
clear_interrupt_cause(StatusNeedsRead);
return
((received_data_ & NoValueMask) ? 0x00 : 0x01) |
((next_transmission_ == NoValueMask) ? 0x02 : 0x00) |
(data_carrier_detect.read() ? 0x04 : 0x00) |
(clear_to_send.read() ? 0x08 : 0x00) |
(interrupt_request_ ? 0x80 : 0x00)
(interrupt_causes_ ? 0x80 : 0x00)
;
// b0: receive data full.
@ -54,7 +49,7 @@ void ACIA::write(int address, uint8_t value) {
if(address&1) {
next_transmission_ = value;
consider_transmission();
interrupt_request_ = false;
clear_interrupt_cause(TransmitNeedsWrite);
} else {
if((value&3) == 3) {
transmit.reset_writing();
@ -114,7 +109,7 @@ void ACIA::run_for(HalfCycles length) {
} else {
transmit.advance_writer(transmit_advance);
update_clocking_observer();
interrupt_request_ |= transmit_interrupt_enabled_;
if(transmit_interrupt_enabled_) add_interrupt_cause(TransmitNeedsWrite);
}
} else {
transmit.advance_writer(transmit_advance);
@ -143,11 +138,7 @@ void ACIA::consider_transmission() {
// Output all that.
const int total_bits = expected_bits();
if(!next_transmission_) {
printf("");
}
transmit.write(divider_ * 2, total_bits, transmission);
printf("Transmitted %02x [%03x]\n", next_transmission_, transmission);
// Mark the transmit register as empty again.
next_transmission_ = NoValueMask;
@ -168,7 +159,7 @@ ClockingHint::Preference ACIA::preferred_clocking() {
}
bool ACIA::get_interrupt_line() const {
return interrupt_request_;
return interrupt_causes_;
}
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) {
bits_received_ = 0;
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();
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();
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::Preference preferred_clocking() final;
struct InterruptDelegate {
virtual void acia6850_did_change_interrupt_status(ACIA *acia) = 0;
};
void set_interrupt_delegate(InterruptDelegate *delegate);
private:
int divider_ = 1;
enum class Parity {
@ -86,12 +91,20 @@ class ACIA: public ClockingHint::Source, private Serial::Line::ReadDelegate {
bool receive_interrupt_enabled_ = false;
bool transmit_interrupt_enabled_ = false;
bool interrupt_request_ = false;
HalfCycles transmit_clock_rate_;
HalfCycles receive_clock_rate_;
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;
};
}