From c733a4dbf8d1432df4e939343859db6a5db0f084 Mon Sep 17 00:00:00 2001
From: Thomas Harte <thomas.harte@gmail.com>
Date: Fri, 23 Jul 2021 21:58:52 -0400
Subject: [PATCH] Beefs up interrupt awareness.

---
 Components/6526/6526.hpp                      |  1 +
 .../Implementation/6526Implementation.hpp     | 60 +++++++++++++++----
 .../6526/Implementation/6526Storage.hpp       |  5 +-
 3 files changed, 53 insertions(+), 13 deletions(-)

diff --git a/Components/6526/6526.hpp b/Components/6526/6526.hpp
index 43a735f11..6026de66d 100644
--- a/Components/6526/6526.hpp
+++ b/Components/6526/6526.hpp
@@ -63,6 +63,7 @@ template <typename PortHandlerT, Personality personality> class MOS6526:
 		template <int port> void set_port_output();
 		template <int port> uint8_t get_port_input();
 		void update_interrupts();
+		void posit_interrupt(uint8_t mask);
 };
 
 }
diff --git a/Components/6526/Implementation/6526Implementation.hpp b/Components/6526/Implementation/6526Implementation.hpp
index 6cb226fd8..8a3571faf 100644
--- a/Components/6526/Implementation/6526Implementation.hpp
+++ b/Components/6526/Implementation/6526Implementation.hpp
@@ -27,8 +27,20 @@ template <int port> uint8_t MOS6526<BusHandlerT, personality>::get_port_input()
 	return (input & ~data_direction_[port]) | (output_[port] & data_direction_[port]);
 }
 
+template <typename BusHandlerT, Personality personality>
+void MOS6526<BusHandlerT, personality>::posit_interrupt(uint8_t mask) {
+	interrupt_state_ |= mask;
+	update_interrupts();
+}
+
 template <typename BusHandlerT, Personality personality>
 void MOS6526<BusHandlerT, personality>::update_interrupts() {
+	if(interrupt_state_ & interrupt_control_) {
+		interrupt_state_ |= 0x80;
+
+		printf("6526 should signal interrupt\n");
+		assert(false);
+	}
 }
 
 template <typename BusHandlerT, Personality personality>
@@ -67,8 +79,12 @@ void MOS6526<BusHandlerT, personality>::write(int address, uint8_t value) {
 		// upon a write to the LSB.
 		case 8:
 			if constexpr (personality == Personality::P8250) {
-				tod_ = (tod_ & 0xffff00) | uint32_t(value);
-				tod_increment_mask_ = uint32_t(~0);
+				if(control_[1] & 0x80) {
+					tod_alarm_ = (tod_alarm_ & 0xffff00) | uint32_t(value);
+				} else {
+					tod_ = (tod_ & 0xffff00) | uint32_t(value);
+					tod_increment_mask_ = uint32_t(~0);
+				}
 			} else {
 				printf("6526 TOD clock not implemented\n");
 				assert(false);
@@ -76,8 +92,12 @@ void MOS6526<BusHandlerT, personality>::write(int address, uint8_t value) {
 		break;
 		case 9:
 			if constexpr (personality == Personality::P8250) {
-				tod_ = (tod_ & 0xff00ff) | uint32_t(value << 8);
-				tod_increment_mask_ = 0;
+				if(control_[1] & 0x80) {
+					tod_alarm_ = (tod_alarm_ & 0xff00ff) | uint32_t(value << 8);
+				} else {
+					tod_ = (tod_ & 0xff00ff) | uint32_t(value << 8);
+					tod_increment_mask_ = 0;
+				}
 			} else {
 				printf("6526 TOD clock not implemented\n");
 				assert(false);
@@ -85,8 +105,12 @@ void MOS6526<BusHandlerT, personality>::write(int address, uint8_t value) {
 		break;
 		case 10:
 			if constexpr (personality == Personality::P8250) {
-				tod_ = (tod_ & 0x00ffff) | uint32_t(value << 16);
-				tod_increment_mask_ = 0;
+				if(control_[1] & 0x80) {
+					tod_alarm_ = (tod_alarm_ & 0x00ffff) | uint32_t(value << 16);
+				} else {
+					tod_ = (tod_ & 0x00ffff) | uint32_t(value << 16);
+					tod_increment_mask_ = 0;
+				}
 			} else {
 				printf("6526 TOD clock not implemented\n");
 				assert(false);
@@ -101,10 +125,14 @@ void MOS6526<BusHandlerT, personality>::write(int address, uint8_t value) {
 		break;
 
 		// Interrupt control.
-		case 13:
-			interrupt_control_ = value;
+		case 13: {
+			if(interrupt_control_ & 0x80) {
+				interrupt_control_ |= value & 0x7f;
+			} else {
+				interrupt_control_ &= ~(value & 0x7f);
+			}
 			update_interrupts();
-		break;
+		} break;
 
 		// Control.
 		case 14:
@@ -126,7 +154,6 @@ uint8_t MOS6526<BusHandlerT, personality>::read(int address) {
 	switch(address) {
 		case 0:		return get_port_input<0>();
 		case 1:		return get_port_input<1>();
-		case 13:	return interrupt_control_;
 
 		case 2: case 3:
 		return data_direction_[address - 2];
@@ -137,6 +164,14 @@ uint8_t MOS6526<BusHandlerT, personality>::read(int address) {
 		case 6:	return uint8_t(counters_[1].value >> 0);
 		case 7:	return uint8_t(counters_[1].value >> 0);
 
+		// Interrupt state.
+		case 13: {
+			const uint8_t result = interrupt_state_;
+			interrupt_state_ = 0;
+			update_interrupts();
+			return result;
+		} break;
+
 		case 14: case 15:
 		return control_[address - 14];
 
@@ -211,14 +246,17 @@ void MOS6526<BusHandlerT, personality>::advance_tod(int count) {
 	if constexpr(personality == Personality::P8250) {
 		// The 8250 uses a simple binary counter to replace the
 		// 6526's time-of-day clock. So this is easy.
+		const uint32_t distance_to_alarm_ = (tod_alarm_ - tod_) & 0xffffff;
 		tod_ += uint32_t(count) & tod_increment_mask_;
+		if(distance_to_alarm_ <= uint32_t(count)) {
+			posit_interrupt(0x04);
+		}
 	} else {
 		// The 6526 uses a time-of-day clock. This may or may not
 		// be accurate.
 	}
 }
 
-
 }
 }
 
diff --git a/Components/6526/Implementation/6526Storage.hpp b/Components/6526/Implementation/6526Storage.hpp
index 630e6e9af..8c76976de 100644
--- a/Components/6526/Implementation/6526Storage.hpp
+++ b/Components/6526/Implementation/6526Storage.hpp
@@ -17,14 +17,15 @@ struct MOS6526Storage {
 
 	uint8_t output_[2] = {0, 0};
 	uint8_t data_direction_[2] = {0, 0};
+
 	uint8_t interrupt_control_ = 0;
+	uint8_t interrupt_state_ = 0;
+
 	uint8_t control_[2] = {0, 0};
 
 	uint32_t tod_increment_mask_ = uint32_t(~0);
 	uint32_t tod_latch_ = 0;
 	uint32_t tod_ = 0;
-
-	bool write_tod_alarm_ = false;
 	uint32_t tod_alarm_ = 0;
 
 	struct Counter {