From adfdfa205f666538dbc18fe15102c25c738e4cc6 Mon Sep 17 00:00:00 2001
From: Thomas Harte <thomas.harte@gmail.com>
Date: Fri, 12 Feb 2021 18:42:12 -0500
Subject: [PATCH] Starts to establish the means by which I'll implement ADB
 devices.

---
 Machines/Apple/ADB/Bus.cpp                    | 48 +++++++++++--------
 Machines/Apple/ADB/Bus.hpp                    | 29 ++++++++---
 Machines/Apple/ADB/ReactiveDevice.cpp         | 21 ++++++++
 Machines/Apple/ADB/ReactiveDevice.hpp         | 35 ++++++++++++++
 .../Clock Signal.xcodeproj/project.pbxproj    |  8 ++++
 5 files changed, 115 insertions(+), 26 deletions(-)
 create mode 100644 Machines/Apple/ADB/ReactiveDevice.cpp
 create mode 100644 Machines/Apple/ADB/ReactiveDevice.hpp

diff --git a/Machines/Apple/ADB/Bus.cpp b/Machines/Apple/ADB/Bus.cpp
index fc424013a..82cfadc91 100644
--- a/Machines/Apple/ADB/Bus.cpp
+++ b/Machines/Apple/ADB/Bus.cpp
@@ -14,17 +14,12 @@ Bus::Bus(HalfCycles clock_speed) : half_cycles_to_microseconds_(1'000'000.0 / cl
 
 void Bus::run_for(HalfCycles duration) {
 	time_in_state_ += duration;
+	time_since_get_state_ += duration;
 }
 
-size_t Bus::add_device() {
-	const size_t id = next_device_id_;
-	++next_device_id_;
-	return id;
-}
-
-void Bus::set_device_output(size_t device, bool output) {
+void Bus::set_device_output(size_t device_id, bool output) {
 	// Modify the all-devices bus state.
-	bus_state_[device] = output;
+	bus_state_[device_id] = output;
 
 	// React to signal edges only.
 	const bool data_level = get_state();
@@ -45,12 +40,12 @@ void Bus::set_device_output(size_t device, bool output) {
 			//	50–72 µs		0
 			//	300 µs			service request
 			if(low_microseconds > 1040.0) {
-				for(auto observer: observers_) {
-					observer->adb_bus_did_observe_event(this, Event::Reset);
+				for(auto device: devices_) {
+					device->adb_bus_did_observe_event(Event::Reset);
 				}
 			} else if(low_microseconds >= 560.0) {
-				for(auto observer: observers_) {
-					observer->adb_bus_did_observe_event(this, Event::Attention);
+				for(auto device: devices_) {
+					device->adb_bus_did_observe_event(Event::Attention);
 				}
 				shift_register_ = 1;
 			} else if(low_microseconds < 50.0) {
@@ -58,12 +53,12 @@ void Bus::set_device_output(size_t device, bool output) {
 			} else if(low_microseconds < 72.0) {
 				shift(0);
 			} else if(low_microseconds >= 291.0 && low_microseconds <= 309.0) {
-				for(auto observer: observers_) {
-					observer->adb_bus_did_observe_event(this, Event::ServiceRequest);
+				for(auto device: devices_) {
+					device->adb_bus_did_observe_event(Event::ServiceRequest);
 				}
 			} else {
-				for(auto observer: observers_) {
-					observer->adb_bus_did_observe_event(this, Event::Unrecognised);
+				for(auto device: devices_) {
+					device->adb_bus_did_observe_event(Event::Unrecognised);
 				}
 			}
 		}
@@ -77,17 +72,30 @@ void Bus::shift(unsigned int value) {
 
 	// Trigger a byte whenever a start bit hits bit 9.
 	if(shift_register_ & 0x200) {
-		for(auto observer: observers_) {
-			observer->adb_bus_did_observe_event(this, Event::Byte, uint8_t(shift_register_ >> 1));
+		for(auto device: devices_) {
+			device->adb_bus_did_observe_event(Event::Byte, uint8_t(shift_register_ >> 1));
 		}
 		shift_register_ = 0;
 	}
 }
 
 bool Bus::get_state() const {
+	const auto microseconds = time_since_get_state_.as<double>() * half_cycles_to_microseconds_;
+	time_since_get_state_ = HalfCycles(0);
+
+	for(auto device: devices_) {
+		device->advance_state(microseconds);
+	}
 	return bus_state_.all();
 }
 
-void Bus::add_observer(Observer *observer) {
-	observers_.push_back(observer);
+size_t Bus::add_device() {
+	const size_t id = next_device_id_;
+	++next_device_id_;
+	return id;
+}
+
+size_t Bus::add_device(Device *device) {
+	devices_.push_back(device);
+	return add_device();
 }
diff --git a/Machines/Apple/ADB/Bus.hpp b/Machines/Apple/ADB/Bus.hpp
index 1b7e1b102..48d7c56cf 100644
--- a/Machines/Apple/ADB/Bus.hpp
+++ b/Machines/Apple/ADB/Bus.hpp
@@ -58,6 +58,15 @@ inline Command decode_command(uint8_t code) {
 	The ADB bus models the data line of the ADB bus; it allows multiple devices to
 	post their current data level, or read the current level, and also offers a tokenised
 	version of all activity on the bus.
+
+	In implementation terms, two types of device are envisaged:
+
+		* proactive devices, which use @c add_device() and then merely @c set_device_output
+		and @c get_state() as required, according to their own tracking of time; and
+
+		* reactive devices, which use @c add_device(Device*) and then merely react to
+		@c adb_bus_did_observe_event and @c advance_state in order to
+		update @c set_device_output.
 */
 class Bus {
 	public:
@@ -77,7 +86,7 @@ class Bus {
 		/*!
 			Sets the current data line output for @c device.
 		*/
-		void set_device_output(size_t device, bool output);
+		void set_device_output(size_t device_id, bool output);
 
 		/*!
 			@returns The current state of the ADB data line.
@@ -93,20 +102,28 @@ class Bus {
 			Unrecognised
 		};
 
-		struct Observer {
+		struct Device {
 			/// Reports to an observer that @c event was observed in the activity
 			/// observed on this bus. If this was a byte event, that byte's value is given as @c value.
-			virtual void adb_bus_did_observe_event(Bus *, Event event, uint8_t value = 0xff);
+			virtual void adb_bus_did_observe_event(Event event, uint8_t value = 0xff) = 0;
+
+			/// Requests that the device update itself @c microseconds and, if necessary, post a
+			/// new value ot @c set_device_output. This will be called only when the bus needs
+			/// to reevaluate its current level. It cannot reliably be used to track the timing between
+			/// observed events.
+			virtual void advance_state(double microseconds) = 0;
 		};
 		/*!
-			Adds an observer.
+			Adds a device.
 		*/
-		void add_observer(Observer *);
+		size_t add_device(Device *);
 
 	private:
 		HalfCycles time_in_state_;
+		mutable HalfCycles time_since_get_state_;
+
 		double half_cycles_to_microseconds_ = 1.0;
-		std::vector<Observer *> observers_;
+		std::vector<Device *> devices_;
 		unsigned int shift_register_ = 0;
 		bool data_level_ = true;
 
diff --git a/Machines/Apple/ADB/ReactiveDevice.cpp b/Machines/Apple/ADB/ReactiveDevice.cpp
new file mode 100644
index 000000000..67396abf2
--- /dev/null
+++ b/Machines/Apple/ADB/ReactiveDevice.cpp
@@ -0,0 +1,21 @@
+//
+//  ReactiveDevice.cpp
+//  Clock Signal
+//
+//  Created by Thomas Harte on 12/02/2021.
+//  Copyright © 2021 Thomas Harte. All rights reserved.
+//
+
+#include "ReactiveDevice.hpp"
+
+using namespace Apple::ADB;
+
+ReactiveDevice::ReactiveDevice(Apple::ADB::Bus &bus) : device_id_(bus.add_device(this)) {}
+
+void ReactiveDevice::post_response(const std::vector<uint8_t> &&response) {
+	response_ = std::move(response);
+}
+
+void ReactiveDevice::advance_state(double microseconds) {
+	(void)microseconds;
+}
diff --git a/Machines/Apple/ADB/ReactiveDevice.hpp b/Machines/Apple/ADB/ReactiveDevice.hpp
new file mode 100644
index 000000000..e9c92494d
--- /dev/null
+++ b/Machines/Apple/ADB/ReactiveDevice.hpp
@@ -0,0 +1,35 @@
+//
+//  ReactiveDevice.hpp
+//  Clock Signal
+//
+//  Created by Thomas Harte on 12/02/2021.
+//  Copyright © 2021 Thomas Harte. All rights reserved.
+//
+
+#ifndef ReactiveDevice_hpp
+#define ReactiveDevice_hpp
+
+#include "Bus.hpp"
+
+#include <cstddef>
+#include <vector>
+
+namespace Apple {
+namespace ADB {
+
+class ReactiveDevice: public Bus::Device {
+	protected:
+		ReactiveDevice(Bus &bus);
+
+		void post_response(const std::vector<uint8_t> &&response);
+		void advance_state(double microseconds) override;
+
+	private:
+		const size_t device_id_;
+		std::vector<uint8_t> response_;
+};
+
+}
+}
+
+#endif /* ReactiveDevice_hpp */
diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj
index 428312bd6..1dafeca2a 100644
--- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj	
+++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj	
@@ -172,6 +172,8 @@
 		4B2BFDB21DAEF5FF001A68B8 /* Video.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B2BFDB01DAEF5FF001A68B8 /* Video.cpp */; };
 		4B2C45421E3C3896002A2389 /* cartridge.png in Resources */ = {isa = PBXBuildFile; fileRef = 4B2C45411E3C3896002A2389 /* cartridge.png */; };
 		4B2E2D9D1C3A070400138695 /* Electron.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B2E2D9B1C3A070400138695 /* Electron.cpp */; };
+		4B2E86B725D7490E0024F1E9 /* ReactiveDevice.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B2E86B525D7490E0024F1E9 /* ReactiveDevice.cpp */; };
+		4B2E86B825D7490E0024F1E9 /* ReactiveDevice.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B2E86B525D7490E0024F1E9 /* ReactiveDevice.cpp */; };
 		4B302184208A550100773308 /* DiskII.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B302183208A550100773308 /* DiskII.cpp */; };
 		4B302185208A550100773308 /* DiskII.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B302183208A550100773308 /* DiskII.cpp */; };
 		4B30512D1D989E2200B4FED8 /* Drive.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B30512B1D989E2200B4FED8 /* Drive.cpp */; };
@@ -1116,6 +1118,8 @@
 		4B2C455C1EC9442600FC74DD /* RegisterSizes.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = RegisterSizes.hpp; sourceTree = "<group>"; };
 		4B2E2D9B1C3A070400138695 /* Electron.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Electron.cpp; path = Electron/Electron.cpp; sourceTree = "<group>"; };
 		4B2E2D9C1C3A070400138695 /* Electron.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Electron.hpp; path = Electron/Electron.hpp; sourceTree = "<group>"; };
+		4B2E86B525D7490E0024F1E9 /* ReactiveDevice.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ReactiveDevice.cpp; sourceTree = "<group>"; };
+		4B2E86B625D7490E0024F1E9 /* ReactiveDevice.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ReactiveDevice.hpp; sourceTree = "<group>"; };
 		4B302182208A550100773308 /* DiskII.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = DiskII.hpp; sourceTree = "<group>"; };
 		4B302183208A550100773308 /* DiskII.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DiskII.cpp; sourceTree = "<group>"; };
 		4B30512B1D989E2200B4FED8 /* Drive.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Drive.cpp; sourceTree = "<group>"; };
@@ -4189,6 +4193,8 @@
 			children = (
 				4BCE1DEF25D4C3FA00AE7A2B /* Bus.cpp */,
 				4BCE1DF025D4C3FA00AE7A2B /* Bus.hpp */,
+				4B2E86B525D7490E0024F1E9 /* ReactiveDevice.cpp */,
+				4B2E86B625D7490E0024F1E9 /* ReactiveDevice.hpp */,
 			);
 			path = ADB;
 			sourceTree = "<group>";
@@ -5108,6 +5114,7 @@
 				4B055A991FAE85CB0060FFFF /* DiskController.cpp in Sources */,
 				4B25B5F925BD083C00362C84 /* DiskIIDrive.cpp in Sources */,
 				4B055ACC1FAE9B030060FFFF /* Electron.cpp in Sources */,
+				4B2E86B825D7490E0024F1E9 /* ReactiveDevice.cpp in Sources */,
 				4B74CF822312FA9C00500CE8 /* HFV.cpp in Sources */,
 				4B8318B022D3E531006DB630 /* AppleII.cpp in Sources */,
 				4B1B58F7246CC4E8009C171E /* State.cpp in Sources */,
@@ -5284,6 +5291,7 @@
 				4BE21219253FCE9C00435408 /* AppleIIgs.cpp in Sources */,
 				4BCE0060227D39AB000CA200 /* Video.cpp in Sources */,
 				4B0ACC2E23775819008902D0 /* TIA.cpp in Sources */,
+				4B2E86B725D7490E0024F1E9 /* ReactiveDevice.cpp in Sources */,
 				4B74CF85231370BC00500CE8 /* MacintoshVolume.cpp in Sources */,
 				4B4518A51F75FD1C00926311 /* SSD.cpp in Sources */,
 				4B55CE5F1C3B7D960093A61B /* MachineDocument.swift in Sources */,