#include "pch.h"
#include "MC6850.h"

#include <cassert>

bool EightBit::mc6850::selected() {
	return raised(CS0()) && raised(CS1()) && lowered(CS2());
}

void EightBit::mc6850::reset() {
	if (!m_powered) {
		raise(IRQ());
		raise(RTS());
	}
	m_statusRDRF = false;
	m_statusTDRE = true;
	m_statusOVRN = false;
	lower(DCD());
	m_powered = true;
	m_statusRead = false;
}

bool EightBit::mc6850::tick() {

	if (!(powered() && raised(E()) && selected()))
		return false;

	Accessing.fire(EventArgs::empty());

	const bool writing = lowered(RW());
	const bool reading = !writing;

	const bool registers = lowered(RS());
	const bool transferring = !registers;

	if (registers) {
		if (writing) {
			m_counterDivide = (CounterDivideSelect)(DATA() & (CR0 | CR1));
			if (m_counterDivide == MasterReset) {
				reset();
			} else {
				m_wordSelect = (WordSelect)((DATA() & (CR2 | CR3 | CR4)) >> 2);
				m_transmitControl = (TransmitterControl)((DATA() & (CR5 | CR6)) >> 5);
				m_receiveControl = (ReceiveControl)((DATA() & CR7) >> 7);
				match(RTS(), transmitReadyHigh());
			}
		} else {
			DATA() = status();
		}
	} else {
		raise(IRQ());
		if (writing)
			startTransmit();
		else
			completeReceive();
	}

	// Catch the transition to lost carrier
	if (lowered(m_oldDCD) && raised(DCD())) {
		raise(IRQ());
		m_statusRead = false;
	}
	m_oldDCD = DCD();

	Accessed.fire(EventArgs::empty());
	return true;
}

uint8_t EightBit::mc6850::status() {
	uint8_t status = 0;
	setFlag(status, STATUS_RDRF, m_statusRDRF);
	setFlag(status, STATUS_TDRE, m_statusTDRE);
	setFlag(status, STATUS_DCD, raised(DCD()));
	setFlag(status, STATUS_CTS, raised(CTS()));
	clearFlag(status, STATUS_FE);
	setFlag(status, STATUS_OVRN, m_statusOVRN);
	clearFlag(status, STATUS_PE);
	setFlag(status, STATUS_IRQ, lowered(IRQ()));
	return status;
}

std::string EightBit::mc6850::dumpStatus() {
	const auto value = status();
	std::string returned;
	returned += "(";
	returned += (value & STATUS_IRQ) ? "IRQ" : "- ";
	returned += (value & STATUS_PE) ? "PE " : "- ";
	returned += (value & STATUS_OVRN) ? "OVRN " : "- ";
	returned += (value & STATUS_FE) ? "FE " : "- ";
	returned += (value & STATUS_CTS) ? "CTS " : "- ";
	returned += (value & STATUS_DCD) ? "DCD " : "- ";
	returned += (value & STATUS_TDRE) ? "TDRE " : "- ";
	returned += (value & STATUS_RDRF) ? "RDRF " : "- ";
	returned += ") ";
	return returned;
}

void EightBit::mc6850::markTransmitComplete() {
	m_statusTDRE = lowered(CTS());
	match(IRQ(), !isInterruptRequired());
	Transmitted.fire(EventArgs::empty());
}

void EightBit::mc6850::markReceiveStarting() {
	m_statusOVRN = m_statusRDRF;	// If the RDR was already full, this means we're losing data
	m_statusRDRF = true;
	match(IRQ(), !isInterruptRequired());
	Receiving.fire(EventArgs::empty());
}

void EightBit::mc6850::startTransmit() {
	TDR() = DATA();
	m_statusTDRE = false;
	Transmitting.fire(EventArgs::empty());
}

void EightBit::mc6850::completeReceive() {
	DATA() = RDR();
	m_statusOVRN = m_statusRDRF = false;	// Any existing data overrun is now moot.
	Received.fire(EventArgs::empty());
}

bool EightBit::mc6850::isInterruptRequired() const {
	return isTransmitInterruptRequired() || isReceiveInterruptRequired();
}

bool EightBit::mc6850::isTransmitInterruptRequired() const {
	return m_statusTDRE && transmitInterruptEnabled();
}

bool EightBit::mc6850::isReceiveInterruptRequired() const {
	return m_statusRDRF && receiveInterruptEnabled();
}