// // Bus.hpp // Clock Signal // // Created by Thomas Harte on 10/02/2021. // Copyright © 2021 Thomas Harte. All rights reserved. // #pragma once #include "../../../ClockReceiver/ClockReceiver.hpp" #include #include #include #include namespace Apple::ADB { struct Command { enum class Type { Reset, Flush, Reserved, /// The host wishes the device to store register contents. Listen, /// The host wishes the device to broadcast register contents. Talk }; static constexpr uint8_t AllDevices = 0xff; static constexpr uint8_t NoRegister = 0xff; Type type = Type::Reserved; uint8_t device = AllDevices; uint8_t reg = NoRegister; Command() = default; Command(Type type) : type(type) {} Command(Type type, uint8_t device) : type(type), device(device) {} Command(Type type, uint8_t device, uint8_t reg) : type(type), device(device), reg(reg) {} }; inline std::ostream &operator <<(std::ostream &stream, Command::Type type) { switch(type) { case Command::Type::Reset: stream << "reset"; break; case Command::Type::Flush: stream << "flush"; break; case Command::Type::Listen: stream << "listen"; break; case Command::Type::Talk: stream << "talk"; break; default: stream << "reserved"; break; } return stream; } inline std::ostream &operator <<(std::ostream &stream, Command command) { stream << "Command {"; if(command.device != 0xff) stream << "device " << int(command.device) << ", "; if(command.reg != 0xff) stream << "register " << int(command.reg) << ", "; stream << command.type; stream << "}"; return stream; } /*! @returns The @c Command encoded in @c code. */ inline Command decode_command(uint8_t code) { switch(code & 0x0f) { default: return Command(); case 0: return Command(Command::Type::Reset); case 1: return Command(Command::Type::Flush, code >> 4); case 8: case 9: case 10: case 11: return Command(Command::Type::Listen, code >> 4, code & 3); case 12: case 13: case 14: case 15: return Command(Command::Type::Talk, code >> 4, code & 3); } } /*! 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: Bus(HalfCycles clock_speed); /*! Advances time; ADB is a clocked serial signal. */ void run_for(HalfCycles); /*! Adds a device to the bus, returning the index it should use to refer to itself in subsequent calls to set_device_output. */ size_t add_device(); /*! Sets the current data line output for @c device. */ void set_device_output(size_t device_id, bool output); /*! @returns The current state of the ADB data line. */ bool get_state() const; enum class Event { Reset, Attention, Byte, ServiceRequest, Unrecognised }; 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(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, bool current_level) = 0; }; /*! Adds a device. */ 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 devices_; unsigned int shift_register_ = 0; unsigned int start_target_ = 8; bool data_level_ = true; // ADB addressing supports at most 16 devices but that doesn't include // the controller. So assume a maximum of 17 connected devices. std::bitset<17> bus_state_{0xffffffff}; size_t next_device_id_ = 0; inline void shift(unsigned int); enum class Phase { PacketCapture, AttentionCapture } phase_ = Phase::AttentionCapture; }; }