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:
parent
c0c2b5e3a9
commit
ecfe68d70f
@ -64,12 +64,8 @@ template <typename PortHandlerT, Personality personality> class MOS6526:
|
||||
/// Sets the current state of the CNT input.
|
||||
void set_cnt_input(bool active);
|
||||
|
||||
/// Provides the serial input bit.
|
||||
Serial::Line serial_input;
|
||||
|
||||
/// Optionally sets CNT to automatically toggle based on the events
|
||||
/// generated by @c serial_input.
|
||||
void set_cnt_tied_to_input(bool);
|
||||
/// Provides both the serial input bit and an additional source of CNT.
|
||||
Serial::Line<true> serial_input;
|
||||
|
||||
/// Sets the current state of the FLG input.
|
||||
void set_flag_input(bool low);
|
||||
|
@ -148,7 +148,7 @@ uint8_t ACIA::parity(uint8_t value) {
|
||||
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
|
||||
// the largest transmission symbol.
|
||||
++bits_received_;
|
||||
|
@ -18,7 +18,7 @@
|
||||
namespace Motorola {
|
||||
namespace ACIA {
|
||||
|
||||
class ACIA: public ClockingHint::Source, private Serial::Line::ReadDelegate {
|
||||
class ACIA: public ClockingHint::Source, private Serial::Line<false>::ReadDelegate {
|
||||
public:
|
||||
static constexpr const HalfCycles SameAsTransmit = HalfCycles(0);
|
||||
|
||||
@ -77,13 +77,13 @@ class ACIA: public ClockingHint::Source, private Serial::Line::ReadDelegate {
|
||||
void reset();
|
||||
|
||||
// Input lines.
|
||||
Serial::Line receive;
|
||||
Serial::Line clear_to_send;
|
||||
Serial::Line data_carrier_detect;
|
||||
Serial::Line<false> receive;
|
||||
Serial::Line<false> clear_to_send;
|
||||
Serial::Line<false> data_carrier_detect;
|
||||
|
||||
// Output lines.
|
||||
Serial::Line transmit;
|
||||
Serial::Line request_to_send;
|
||||
Serial::Line<false> transmit;
|
||||
Serial::Line<false> request_to_send;
|
||||
|
||||
// ClockingHint::Source.
|
||||
ClockingHint::Preference preferred_clocking() const final;
|
||||
@ -118,7 +118,7 @@ class ACIA: public ClockingHint::Source, private Serial::Line::ReadDelegate {
|
||||
HalfCycles transmit_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;
|
||||
void update_interrupt_line();
|
||||
|
@ -12,11 +12,13 @@
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void Line::advance_writer(HalfCycles cycles) {
|
||||
template <bool include_clock>
|
||||
void Line<include_clock>::advance_writer(HalfCycles cycles) {
|
||||
if(cycles == HalfCycles(0)) return;
|
||||
|
||||
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()) {
|
||||
events_.emplace_back();
|
||||
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();
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
template void Line::write<true, uint8_t>(HalfCycles, uint8_t);
|
||||
template void Line::write<false, uint8_t>(HalfCycles, uint8_t);
|
||||
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() {
|
||||
template <bool include_clock>
|
||||
void Line<include_clock>::reset_writing() {
|
||||
remaining_delays_ = 0;
|
||||
events_.clear();
|
||||
}
|
||||
|
||||
bool Line::read() const {
|
||||
template <bool include_clock>
|
||||
bool Line<include_clock>::read() const {
|
||||
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_bit_length_ = bit_length;
|
||||
read_delegate_bit_length_.simplify();
|
||||
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
|
||||
// zero and this isn't zero.
|
||||
if(!read_delegate_) return;
|
||||
@ -162,7 +163,36 @@ void Line::update_delegate(bool level) {
|
||||
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;
|
||||
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);
|
||||
|
@ -25,7 +25,7 @@ namespace Serial {
|
||||
get ahead of the writer. If the writer posts events behind the reader they will simply be
|
||||
given instanteous effect.
|
||||
*/
|
||||
class Line {
|
||||
template <bool include_clock> class Line {
|
||||
public:
|
||||
void set_writer_clock_rate(HalfCycles clock_rate);
|
||||
|
||||
|
@ -42,7 +42,7 @@
|
||||
|
||||
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.
|
||||
}
|
||||
|
||||
|
@ -77,7 +77,7 @@ struct KeyboardMapper: public MachineTypes::MappedKeyboardMachine::KeyboardMappe
|
||||
|
||||
class Keyboard {
|
||||
public:
|
||||
Keyboard(Serial::Line &output);
|
||||
Keyboard(Serial::Line<true> &output);
|
||||
|
||||
// enum Lines: uint8_t {
|
||||
// Data = (1 << 0),
|
||||
@ -106,7 +106,7 @@ class Keyboard {
|
||||
|
||||
uint8_t lines_ = 0;
|
||||
|
||||
Serial::Line &output_;
|
||||
Serial::Line<true> &output_;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -15,7 +15,7 @@
|
||||
|
||||
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));
|
||||
output_line_.set_writer_clock_rate(15625);
|
||||
|
||||
@ -24,7 +24,7 @@ IntelligentKeyboard::IntelligentKeyboard(Serial::Line &input, Serial::Line &outp
|
||||
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.
|
||||
command_ = (command_ >> 1) | (bit << 9);
|
||||
|
||||
|
@ -53,11 +53,11 @@ static_assert(uint16_t(Key::KeypadEnter) == 0x72, "KeypadEnter should have key c
|
||||
keyboard input and output and mouse handling.
|
||||
*/
|
||||
class IntelligentKeyboard:
|
||||
public Serial::Line::ReadDelegate,
|
||||
public Serial::Line<false>::ReadDelegate,
|
||||
public ClockingHint::Source,
|
||||
public Inputs::Mouse {
|
||||
public:
|
||||
IntelligentKeyboard(Serial::Line &input, Serial::Line &output);
|
||||
IntelligentKeyboard(Serial::Line<false> &input, Serial::Line<false> &output);
|
||||
ClockingHint::Preference preferred_clocking() const final;
|
||||
void run_for(HalfCycles duration);
|
||||
|
||||
@ -78,10 +78,10 @@ class IntelligentKeyboard:
|
||||
// MARK: - Serial line state.
|
||||
int bit_count_ = 0;
|
||||
int command_ = 0;
|
||||
Serial::Line &output_line_;
|
||||
Serial::Line<false> &output_line_;
|
||||
|
||||
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.
|
||||
std::vector<uint8_t> command_sequence_;
|
||||
|
Loading…
Reference in New Issue
Block a user