mirror of
https://github.com/TomHarte/CLK.git
synced 2025-04-06 10:38:16 +00:00
Starts to formalise the ADB bus.
This commit is contained in:
parent
46bd20b5e0
commit
17bac4c8cf
93
Machines/Apple/ADB/Bus.cpp
Normal file
93
Machines/Apple/ADB/Bus.cpp
Normal file
@ -0,0 +1,93 @@
|
||||
//
|
||||
// Bus.cpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 10/02/2021.
|
||||
// Copyright © 2021 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "Bus.hpp"
|
||||
|
||||
using namespace Apple::ADB;
|
||||
|
||||
Bus::Bus(HalfCycles clock_speed) : half_cycles_to_microseconds_(1'000'000.0 / clock_speed.as<double>()) {}
|
||||
|
||||
void Bus::run_for(HalfCycles duration) {
|
||||
time_in_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) {
|
||||
// Modify the all-devices bus state.
|
||||
bus_state_[device] = output;
|
||||
|
||||
// React to signal edges only.
|
||||
const bool data_level = get_state();
|
||||
if(data_level_ != data_level) {
|
||||
data_level_ = data_level;
|
||||
|
||||
if(data_level) {
|
||||
// This was a transition to high; classify what just happened according to
|
||||
// the duration of the low period.
|
||||
const double low_microseconds = time_in_state_.as<double>() * half_cycles_to_microseconds_;
|
||||
|
||||
// Low periods:
|
||||
// (partly as adapted from the AN591 data sheet; otherwise from the IIgs reference manual)
|
||||
//
|
||||
// > 1040 µs reset
|
||||
// 560–1040 µs attention
|
||||
// < 50 µs 1
|
||||
// 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);
|
||||
}
|
||||
} else if(low_microseconds >= 560.0) {
|
||||
for(auto observer: observers_) {
|
||||
observer->adb_bus_did_observe_event(this, Event::Attention);
|
||||
}
|
||||
shift_register_ = 1;
|
||||
} else if(low_microseconds < 50.0) {
|
||||
shift(1);
|
||||
} 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);
|
||||
}
|
||||
} else {
|
||||
for(auto observer: observers_) {
|
||||
observer->adb_bus_did_observe_event(this, Event::Unrecognised);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
time_in_state_ = HalfCycles(0);
|
||||
}
|
||||
}
|
||||
|
||||
void Bus::shift(unsigned int value) {
|
||||
shift_register_ = (shift_register_ << 1) | 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));
|
||||
}
|
||||
shift_register_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool Bus::get_state() const {
|
||||
return bus_state_.all();
|
||||
}
|
||||
|
||||
void Bus::add_observer(Observer *observer) {
|
||||
observers_.push_back(observer);
|
||||
}
|
75
Machines/Apple/ADB/Bus.hpp
Normal file
75
Machines/Apple/ADB/Bus.hpp
Normal file
@ -0,0 +1,75 @@
|
||||
//
|
||||
// Bus.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 10/02/2021.
|
||||
// Copyright © 2021 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef Bus_hpp
|
||||
#define Bus_hpp
|
||||
|
||||
#include "../../../ClockReceiver/ClockReceiver.hpp"
|
||||
|
||||
#include <bitset>
|
||||
#include <cstddef>
|
||||
#include <vector>
|
||||
|
||||
namespace Apple {
|
||||
namespace ADB {
|
||||
|
||||
/*!
|
||||
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.
|
||||
*/
|
||||
class Bus {
|
||||
public:
|
||||
Bus(HalfCycles clock_speed);
|
||||
|
||||
void run_for(HalfCycles);
|
||||
|
||||
size_t add_device();
|
||||
|
||||
void set_device_output(size_t device, bool output);
|
||||
|
||||
bool get_state() const;
|
||||
|
||||
enum class Event {
|
||||
Reset,
|
||||
Attention,
|
||||
Byte,
|
||||
ServiceRequest,
|
||||
|
||||
Unrecognised
|
||||
};
|
||||
|
||||
struct Observer {
|
||||
/// Reports to an observer that @c event was observed in the activity
|
||||
/// observed on this bus.
|
||||
virtual void adb_bus_did_observe_event(Bus *, Event event, uint8_t value = 0xff);
|
||||
};
|
||||
/*!
|
||||
Adds an observer.
|
||||
*/
|
||||
void add_observer(Observer *);
|
||||
|
||||
private:
|
||||
HalfCycles time_in_state_;
|
||||
double half_cycles_to_microseconds_ = 1.0;
|
||||
std::vector<Observer *> observers_;
|
||||
unsigned int shift_register_ = 0;
|
||||
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);
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* Bus_hpp */
|
@ -39,7 +39,7 @@ enum class MicrocontrollerFlags: uint8_t {
|
||||
|
||||
}
|
||||
|
||||
GLU::GLU() : executor_(*this) {}
|
||||
GLU::GLU() : executor_(*this), bus_(HalfCycles(1'789'772)), controller_id_(bus_.add_device()) {}
|
||||
|
||||
// MARK: - External interface.
|
||||
|
||||
@ -168,50 +168,14 @@ void GLU::set_port_output(int port, uint8_t value) {
|
||||
// printf("Select GLU register: %d [%02x]\n", value & 0xf, value);
|
||||
register_address_ = value & 0xf;
|
||||
break;
|
||||
case 3: {
|
||||
case 3:
|
||||
// printf("IIe KWS: %d\n", (value >> 6)&3);
|
||||
// printf("ADB data line output: %d\n", (value >> 3)&1);
|
||||
|
||||
// Output is inverted respective to input; the microcontroller
|
||||
// sets a value of '1' in order to pull the ADB bus low.
|
||||
const bool new_adb_level = !(value & 0x08);
|
||||
|
||||
// React to signal edges only.
|
||||
if(new_adb_level != adb_level_) {
|
||||
if(new_adb_level) {
|
||||
// This was a transition to high; classify what just happened according to
|
||||
// the duration of the low period.
|
||||
const uint64_t low_microseconds = (low_period_.as<uint64_t>() * uint64_t(800000)) / uint64_t(715909);
|
||||
|
||||
// Low periods:
|
||||
// (partly as adapted from the AN591 data sheet; otherwise from the IIgs reference manual)
|
||||
//
|
||||
// > 1040 µs reset
|
||||
// 560–1040 µs attention
|
||||
// < 50 µs 1
|
||||
// 50–72 µs 0
|
||||
// 300 µs service request
|
||||
printf("%llu -> ", low_microseconds);
|
||||
if(low_microseconds > 1040) {
|
||||
printf("!!! Reset\n");
|
||||
} else if(low_microseconds >= 560) {
|
||||
printf("!!! Attention\n");
|
||||
} else if(low_microseconds < 50) {
|
||||
printf("!!! bit: 1\n");
|
||||
} else if(low_microseconds < 72) {
|
||||
printf("!!! bit: 0\n");
|
||||
} else if(low_microseconds >= 291 && low_microseconds <= 309) {
|
||||
printf("!!! SRQ\n");
|
||||
} else {
|
||||
printf("!!! Rejected\n");
|
||||
}
|
||||
|
||||
// TODO: eliminate total_period_ ?
|
||||
total_period_ = low_period_ = Cycles(0);
|
||||
}
|
||||
adb_level_ = new_adb_level;
|
||||
}
|
||||
} break;
|
||||
bus_.set_device_output(controller_id_, !(value & 0x08));
|
||||
break;
|
||||
|
||||
default: assert(false);
|
||||
}
|
||||
@ -238,7 +202,7 @@ uint8_t GLU::get_port_input(int port) {
|
||||
return 0x06;
|
||||
case 2:
|
||||
// printf("ADB data line input, etc\n");
|
||||
return adb_level_ ? 0x80 : 0x00;
|
||||
return bus_.get_state() ? 0x80 : 0x00;
|
||||
case 3:
|
||||
// printf("ADB data line output, etc\n");
|
||||
return 0x00;
|
||||
@ -249,8 +213,5 @@ uint8_t GLU::get_port_input(int port) {
|
||||
}
|
||||
|
||||
void GLU::run_ports_for(Cycles cycles) {
|
||||
total_period_ += cycles;
|
||||
if(!adb_level_) {
|
||||
low_period_ += cycles;
|
||||
}
|
||||
bus_.run_for(cycles);
|
||||
}
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
#include "../../../InstructionSets/M50740/Executor.hpp"
|
||||
#include "../ADB/Bus.hpp"
|
||||
|
||||
namespace Apple {
|
||||
namespace IIgs {
|
||||
@ -54,9 +55,10 @@ class GLU: public InstructionSet::M50740::PortHandler {
|
||||
|
||||
uint8_t status_ = 0x00;
|
||||
|
||||
// TODO: this should be per peripheral. But I'm putting it here for now as an exploratory step.
|
||||
bool adb_level_ = true;
|
||||
Cycles low_period_, total_period_;
|
||||
Apple::ADB::Bus bus_;
|
||||
size_t controller_id_;
|
||||
|
||||
// TODO: add some other devices, and attach them to the ADB bus.
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -875,6 +875,8 @@
|
||||
4BCE005A227CFFCA000CA200 /* Macintosh.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BCE0058227CFFCA000CA200 /* Macintosh.cpp */; };
|
||||
4BCE005D227D30CC000CA200 /* MemoryPacker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BCE005B227D30CC000CA200 /* MemoryPacker.cpp */; };
|
||||
4BCE0060227D39AB000CA200 /* Video.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BCE005E227D39AB000CA200 /* Video.cpp */; };
|
||||
4BCE1DF125D4C3FA00AE7A2B /* Bus.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BCE1DEF25D4C3FA00AE7A2B /* Bus.cpp */; };
|
||||
4BCE1DF225D4C3FA00AE7A2B /* Bus.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BCE1DEF25D4C3FA00AE7A2B /* Bus.cpp */; };
|
||||
4BCF1FA41DADC3DD0039D2E7 /* Oric.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BCF1FA21DADC3DD0039D2E7 /* Oric.cpp */; };
|
||||
4BD0FBC3233706A200148981 /* CSApplication.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BD0FBC2233706A200148981 /* CSApplication.m */; };
|
||||
4BD191F52191180E0042E144 /* ScanTarget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD191F22191180E0042E144 /* ScanTarget.cpp */; };
|
||||
@ -1860,6 +1862,8 @@
|
||||
4BCE005C227D30CC000CA200 /* MemoryPacker.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = MemoryPacker.hpp; sourceTree = "<group>"; };
|
||||
4BCE005E227D39AB000CA200 /* Video.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Video.cpp; sourceTree = "<group>"; };
|
||||
4BCE005F227D39AB000CA200 /* Video.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Video.hpp; sourceTree = "<group>"; };
|
||||
4BCE1DEF25D4C3FA00AE7A2B /* Bus.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Bus.cpp; sourceTree = "<group>"; };
|
||||
4BCE1DF025D4C3FA00AE7A2B /* Bus.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Bus.hpp; sourceTree = "<group>"; };
|
||||
4BCF1FA21DADC3DD0039D2E7 /* Oric.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Oric.cpp; path = Oric/Oric.cpp; sourceTree = "<group>"; };
|
||||
4BCF1FA31DADC3DD0039D2E7 /* Oric.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Oric.hpp; path = Oric/Oric.hpp; sourceTree = "<group>"; };
|
||||
4BD060A51FE49D3C006E14BE /* Speaker.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Speaker.hpp; sourceTree = "<group>"; };
|
||||
@ -4137,8 +4141,9 @@
|
||||
4BCE0048227CE8CA000CA200 /* Apple */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4BE2120D253FCE9C00435408 /* AppleIIgs */,
|
||||
4BCE1DEE25D4C3FA00AE7A2B /* ADB */,
|
||||
4BCE0049227CE8CA000CA200 /* AppleII */,
|
||||
4BE2120D253FCE9C00435408 /* AppleIIgs */,
|
||||
4BCE0057227CFFCA000CA200 /* Macintosh */,
|
||||
);
|
||||
path = Apple;
|
||||
@ -4179,6 +4184,15 @@
|
||||
path = Macintosh;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4BCE1DEE25D4C3FA00AE7A2B /* ADB */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4BCE1DEF25D4C3FA00AE7A2B /* Bus.cpp */,
|
||||
4BCE1DF025D4C3FA00AE7A2B /* Bus.hpp */,
|
||||
);
|
||||
path = ADB;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4BCF1FA51DADC3E10039D2E7 /* Oric */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -4983,6 +4997,7 @@
|
||||
files = (
|
||||
4B0E04FB1FC9FA3100F43484 /* 9918.cpp in Sources */,
|
||||
4B1B88C9202E469400B67DFF /* MultiJoystickMachine.cpp in Sources */,
|
||||
4BCE1DF225D4C3FA00AE7A2B /* Bus.cpp in Sources */,
|
||||
4B055AAA1FAE85F50060FFFF /* CPM.cpp in Sources */,
|
||||
4B055A9A1FAE85CB0060FFFF /* MFMDiskController.cpp in Sources */,
|
||||
4B0ACC3123775819008902D0 /* TIASound.cpp in Sources */,
|
||||
@ -5355,6 +5370,7 @@
|
||||
4B3051301D98ACC600B4FED8 /* Plus3.cpp in Sources */,
|
||||
4B30512D1D989E2200B4FED8 /* Drive.cpp in Sources */,
|
||||
4BCE005D227D30CC000CA200 /* MemoryPacker.cpp in Sources */,
|
||||
4BCE1DF125D4C3FA00AE7A2B /* Bus.cpp in Sources */,
|
||||
4BCE0051227CE8CA000CA200 /* Video.cpp in Sources */,
|
||||
4B894536201967B4007DE474 /* Z80.cpp in Sources */,
|
||||
4BCA6CC81D9DD9F000C2D7B2 /* CommodoreROM.cpp in Sources */,
|
||||
|
Loading…
x
Reference in New Issue
Block a user