1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-11-25 16:31:42 +00:00

Introduce the principle that a Serial::Line can be two-wire — clock + data.

This commit is contained in:
Thomas Harte 2021-11-06 16:54:20 -07:00
parent c0c2b5e3a9
commit ecfe68d70f
9 changed files with 71 additions and 45 deletions

View File

@ -64,12 +64,8 @@ template <typename PortHandlerT, Personality personality> class MOS6526:
/// Sets the current state of the CNT input. /// Sets the current state of the CNT input.
void set_cnt_input(bool active); void set_cnt_input(bool active);
/// Provides the serial input bit. /// Provides both the serial input bit and an additional source of CNT.
Serial::Line serial_input; Serial::Line<true> serial_input;
/// Optionally sets CNT to automatically toggle based on the events
/// generated by @c serial_input.
void set_cnt_tied_to_input(bool);
/// Sets the current state of the FLG input. /// Sets the current state of the FLG input.
void set_flag_input(bool low); void set_flag_input(bool low);

View File

@ -148,7 +148,7 @@ uint8_t ACIA::parity(uint8_t value) {
return value ^ (parity_ == Parity::Even); return value ^ (parity_ == Parity::Even);
} }
bool ACIA::serial_line_did_produce_bit(Serial::Line *, int bit) { bool ACIA::serial_line_did_produce_bit(Serial::Line<false> *, int bit) {
// Shift this bit into the 11-bit input register; this is big enough to hold // Shift this bit into the 11-bit input register; this is big enough to hold
// the largest transmission symbol. // the largest transmission symbol.
++bits_received_; ++bits_received_;

View File

@ -18,7 +18,7 @@
namespace Motorola { namespace Motorola {
namespace ACIA { namespace ACIA {
class ACIA: public ClockingHint::Source, private Serial::Line::ReadDelegate { class ACIA: public ClockingHint::Source, private Serial::Line<false>::ReadDelegate {
public: public:
static constexpr const HalfCycles SameAsTransmit = HalfCycles(0); static constexpr const HalfCycles SameAsTransmit = HalfCycles(0);
@ -77,13 +77,13 @@ class ACIA: public ClockingHint::Source, private Serial::Line::ReadDelegate {
void reset(); void reset();
// Input lines. // Input lines.
Serial::Line receive; Serial::Line<false> receive;
Serial::Line clear_to_send; Serial::Line<false> clear_to_send;
Serial::Line data_carrier_detect; Serial::Line<false> data_carrier_detect;
// Output lines. // Output lines.
Serial::Line transmit; Serial::Line<false> transmit;
Serial::Line request_to_send; Serial::Line<false> request_to_send;
// ClockingHint::Source. // ClockingHint::Source.
ClockingHint::Preference preferred_clocking() const final; ClockingHint::Preference preferred_clocking() const final;
@ -118,7 +118,7 @@ class ACIA: public ClockingHint::Source, private Serial::Line::ReadDelegate {
HalfCycles transmit_clock_rate_; HalfCycles transmit_clock_rate_;
HalfCycles receive_clock_rate_; HalfCycles receive_clock_rate_;
bool serial_line_did_produce_bit(Serial::Line *line, int bit) final; bool serial_line_did_produce_bit(Serial::Line<false> *line, int bit) final;
bool interrupt_line_ = false; bool interrupt_line_ = false;
void update_interrupt_line(); void update_interrupt_line();

View File

@ -12,11 +12,13 @@
using namespace Serial; using namespace Serial;
void Line::set_writer_clock_rate(HalfCycles clock_rate) { template <bool include_clock>
void Line<include_clock>::set_writer_clock_rate(HalfCycles clock_rate) {
clock_rate_ = clock_rate; clock_rate_ = clock_rate;
} }
void Line::advance_writer(HalfCycles cycles) { template <bool include_clock>
void Line<include_clock>::advance_writer(HalfCycles cycles) {
if(cycles == HalfCycles(0)) return; if(cycles == HalfCycles(0)) return;
const auto integral_cycles = cycles.as_integral(); const auto integral_cycles = cycles.as_integral();
@ -62,7 +64,8 @@ void Line::advance_writer(HalfCycles cycles) {
} }
} }
void Line::write(bool level) { template <bool include_clock>
void Line<include_clock>::write(bool level) {
if(!events_.empty()) { if(!events_.empty()) {
events_.emplace_back(); events_.emplace_back();
events_.back().type = level ? Event::SetHigh : Event::SetLow; events_.back().type = level ? Event::SetHigh : Event::SetLow;
@ -72,7 +75,8 @@ void Line::write(bool level) {
} }
} }
template <bool lsb_first, typename IntT> void Line::write_internal(HalfCycles cycles, int count, IntT levels) { template <bool include_clock>
template <bool lsb_first, typename IntT> void Line<include_clock>::write_internal(HalfCycles cycles, int count, IntT levels) {
remaining_delays_ += count * cycles.as_integral(); remaining_delays_ += count * cycles.as_integral();
auto event = events_.size(); auto event = events_.size();
@ -95,40 +99,37 @@ template <bool lsb_first, typename IntT> void Line::write_internal(HalfCycles cy
} }
} }
void Line::write(HalfCycles cycles, int count, int levels) { template <bool include_clock>
void Line<include_clock>::write(HalfCycles cycles, int count, int levels) {
write_internal<true, int>(cycles, count, levels); write_internal<true, int>(cycles, count, levels);
} }
template <bool lsb_first, typename IntT> void Line::write(HalfCycles cycles, IntT value) { template <bool include_clock>
template <bool lsb_first, typename IntT> void Line<include_clock>::write(HalfCycles cycles, IntT value) {
write_internal<lsb_first, IntT>(cycles, 8 * sizeof(IntT), value); write_internal<lsb_first, IntT>(cycles, 8 * sizeof(IntT), value);
} }
template void Line::write<true, uint8_t>(HalfCycles, uint8_t); template <bool include_clock>
template void Line::write<false, uint8_t>(HalfCycles, uint8_t); void Line<include_clock>::reset_writing() {
template void Line::write<true, uint16_t>(HalfCycles, uint16_t);
template void Line::write<false, uint16_t>(HalfCycles, uint16_t);
template void Line::write<true, uint32_t>(HalfCycles, uint32_t);
template void Line::write<false, uint32_t>(HalfCycles, uint32_t);
template void Line::write<true, uint64_t>(HalfCycles, uint64_t);
template void Line::write<false, uint64_t>(HalfCycles, uint64_t);
void Line::reset_writing() {
remaining_delays_ = 0; remaining_delays_ = 0;
events_.clear(); events_.clear();
} }
bool Line::read() const { template <bool include_clock>
bool Line<include_clock>::read() const {
return level_; return level_;
} }
void Line::set_read_delegate(ReadDelegate *delegate, Storage::Time bit_length) { template <bool include_clock>
void Line<include_clock>::set_read_delegate(ReadDelegate *delegate, Storage::Time bit_length) {
read_delegate_ = delegate; read_delegate_ = delegate;
read_delegate_bit_length_ = bit_length; read_delegate_bit_length_ = bit_length;
read_delegate_bit_length_.simplify(); read_delegate_bit_length_.simplify();
write_cycles_since_delegate_call_ = 0; write_cycles_since_delegate_call_ = 0;
} }
void Line::update_delegate(bool level) { template <bool include_clock>
void Line<include_clock>::update_delegate(bool level) {
// Exit early if there's no delegate, or if the delegate is waiting for // Exit early if there's no delegate, or if the delegate is waiting for
// zero and this isn't zero. // zero and this isn't zero.
if(!read_delegate_) return; if(!read_delegate_) return;
@ -162,7 +163,36 @@ void Line::update_delegate(bool level) {
time_left_in_bit_ -= time_left; time_left_in_bit_ -= time_left;
} }
Cycles::IntType Line::minimum_write_cycles_for_read_delegate_bit() { template <bool include_clock>
Cycles::IntType Line<include_clock>::minimum_write_cycles_for_read_delegate_bit() {
if(!read_delegate_) return 0; if(!read_delegate_) return 0;
return 1 + (read_delegate_bit_length_ * unsigned(clock_rate_.as_integral())).get<int>(); return 1 + (read_delegate_bit_length_ * unsigned(clock_rate_.as_integral())).template get<int>();
} }
//
// Explicitly instantiate the meaningful instances of templates above;
// this class uses templates primarily to keep the interface compact and
// to take advantage of constexpr functionality selection, not so as
// to be generic.
//
template class Serial::Line<true>;
template class Serial::Line<false>;
template void Line<true>::write<true, uint8_t>(HalfCycles, uint8_t);
template void Line<true>::write<false, uint8_t>(HalfCycles, uint8_t);
template void Line<true>::write<true, uint16_t>(HalfCycles, uint16_t);
template void Line<true>::write<false, uint16_t>(HalfCycles, uint16_t);
template void Line<true>::write<true, uint32_t>(HalfCycles, uint32_t);
template void Line<true>::write<false, uint32_t>(HalfCycles, uint32_t);
template void Line<true>::write<true, uint64_t>(HalfCycles, uint64_t);
template void Line<true>::write<false, uint64_t>(HalfCycles, uint64_t);
template void Line<false>::write<true, uint8_t>(HalfCycles, uint8_t);
template void Line<false>::write<false, uint8_t>(HalfCycles, uint8_t);
template void Line<false>::write<true, uint16_t>(HalfCycles, uint16_t);
template void Line<false>::write<false, uint16_t>(HalfCycles, uint16_t);
template void Line<false>::write<true, uint32_t>(HalfCycles, uint32_t);
template void Line<false>::write<false, uint32_t>(HalfCycles, uint32_t);
template void Line<false>::write<true, uint64_t>(HalfCycles, uint64_t);
template void Line<false>::write<false, uint64_t>(HalfCycles, uint64_t);

View File

@ -25,7 +25,7 @@ namespace Serial {
get ahead of the writer. If the writer posts events behind the reader they will simply be get ahead of the writer. If the writer posts events behind the reader they will simply be
given instanteous effect. given instanteous effect.
*/ */
class Line { template <bool include_clock> class Line {
public: public:
void set_writer_clock_rate(HalfCycles clock_rate); void set_writer_clock_rate(HalfCycles clock_rate);

View File

@ -42,7 +42,7 @@
using namespace Amiga; using namespace Amiga;
Keyboard::Keyboard(Serial::Line &output) : output_(output) { Keyboard::Keyboard(Serial::Line<true> &output) : output_(output) {
output_.set_writer_clock_rate(HalfCycles(1'000'000)); // Use µs. output_.set_writer_clock_rate(HalfCycles(1'000'000)); // Use µs.
} }

View File

@ -77,7 +77,7 @@ struct KeyboardMapper: public MachineTypes::MappedKeyboardMachine::KeyboardMappe
class Keyboard { class Keyboard {
public: public:
Keyboard(Serial::Line &output); Keyboard(Serial::Line<true> &output);
// enum Lines: uint8_t { // enum Lines: uint8_t {
// Data = (1 << 0), // Data = (1 << 0),
@ -106,7 +106,7 @@ class Keyboard {
uint8_t lines_ = 0; uint8_t lines_ = 0;
Serial::Line &output_; Serial::Line<true> &output_;
}; };
} }

View File

@ -15,7 +15,7 @@
using namespace Atari::ST; using namespace Atari::ST;
IntelligentKeyboard::IntelligentKeyboard(Serial::Line &input, Serial::Line &output) : output_line_(output) { IntelligentKeyboard::IntelligentKeyboard(Serial::Line<false> &input, Serial::Line<false> &output) : output_line_(output) {
input.set_read_delegate(this, Storage::Time(2, 15625)); input.set_read_delegate(this, Storage::Time(2, 15625));
output_line_.set_writer_clock_rate(15625); output_line_.set_writer_clock_rate(15625);
@ -24,7 +24,7 @@ IntelligentKeyboard::IntelligentKeyboard(Serial::Line &input, Serial::Line &outp
joysticks_.emplace_back(new Joystick); joysticks_.emplace_back(new Joystick);
} }
bool IntelligentKeyboard::serial_line_did_produce_bit(Serial::Line *, int bit) { bool IntelligentKeyboard::serial_line_did_produce_bit(Serial::Line<false> *, int bit) {
// Shift. // Shift.
command_ = (command_ >> 1) | (bit << 9); command_ = (command_ >> 1) | (bit << 9);

View File

@ -53,11 +53,11 @@ static_assert(uint16_t(Key::KeypadEnter) == 0x72, "KeypadEnter should have key c
keyboard input and output and mouse handling. keyboard input and output and mouse handling.
*/ */
class IntelligentKeyboard: class IntelligentKeyboard:
public Serial::Line::ReadDelegate, public Serial::Line<false>::ReadDelegate,
public ClockingHint::Source, public ClockingHint::Source,
public Inputs::Mouse { public Inputs::Mouse {
public: public:
IntelligentKeyboard(Serial::Line &input, Serial::Line &output); IntelligentKeyboard(Serial::Line<false> &input, Serial::Line<false> &output);
ClockingHint::Preference preferred_clocking() const final; ClockingHint::Preference preferred_clocking() const final;
void run_for(HalfCycles duration); void run_for(HalfCycles duration);
@ -78,10 +78,10 @@ class IntelligentKeyboard:
// MARK: - Serial line state. // MARK: - Serial line state.
int bit_count_ = 0; int bit_count_ = 0;
int command_ = 0; int command_ = 0;
Serial::Line &output_line_; Serial::Line<false> &output_line_;
void output_bytes(std::initializer_list<uint8_t> value); void output_bytes(std::initializer_list<uint8_t> value);
bool serial_line_did_produce_bit(Serial::Line *, int bit) final; bool serial_line_did_produce_bit(Serial::Line<false> *, int bit) final;
// MARK: - Command dispatch. // MARK: - Command dispatch.
std::vector<uint8_t> command_sequence_; std::vector<uint8_t> command_sequence_;