diff --git a/Components/6850/6850.cpp b/Components/6850/6850.cpp index d59f5c971..18cd067a4 100644 --- a/Components/6850/6850.cpp +++ b/Components/6850/6850.cpp @@ -102,7 +102,7 @@ void ACIA::run_for(HalfCycles length) { if(write_data_time_remaining) { if(transmit_advance >= write_data_time_remaining) { if(next_transmission_ != NoValue) { - transmit.flush_writing(); + transmit.advance_writer(write_data_time_remaining); consider_transmission(); transmit.advance_writer(transmit_advance - write_data_time_remaining); } else { diff --git a/Components/SerialPort/SerialPort.cpp b/Components/SerialPort/SerialPort.cpp index 5e80b4be9..7611883bf 100644 --- a/Components/SerialPort/SerialPort.cpp +++ b/Components/SerialPort/SerialPort.cpp @@ -30,10 +30,7 @@ void Line::advance_writer(int cycles) { events_.erase(events_.begin(), iterator); if(old_level != level_) { - if(read_delegate_) { - read_delegate_->serial_line_did_change_output(this, Storage::Time(write_cycles_since_delegate_call_, clock_rate_), old_level); - write_cycles_since_delegate_call_ = 0; - } + update_delegate(old_level); } } else { events_.front().delay -= cycles; @@ -41,6 +38,8 @@ void Line::advance_writer(int cycles) { break; } } + + // TODO: some sort of ongoing update_delegate() ? } void Line::write(bool level) { @@ -75,35 +74,45 @@ void Line::reset_writing() { events_.clear(); } -void Line::flush_writing() { - remaining_delays_ = 0; - for(const auto &event : events_) { - bool new_level = level_; - switch(event.type) { - default: break; - case Event::SetHigh: new_level = true; break; - case Event::SetLow: new_level = false; break; - case Event::Delay: - write_cycles_since_delegate_call_ += event.delay; - continue; - } - - if(new_level != level_) { - if(read_delegate_) { - read_delegate_->serial_line_did_change_output(this, Storage::Time(write_cycles_since_delegate_call_, clock_rate_), level_); - write_cycles_since_delegate_call_ = 0; - } - level_ = new_level; - } - } - events_.clear(); -} - bool Line::read() { return level_; } -void Line::set_read_delegate(ReadDelegate *delegate) { +void Line::set_read_delegate(ReadDelegate *delegate, Storage::Time bit_length) { read_delegate_ = delegate; + read_delegate_bit_length_ = bit_length; write_cycles_since_delegate_call_ = 0; } + +void Line::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; + + const int cycles_to_forward = write_cycles_since_delegate_call_; + write_cycles_since_delegate_call_ = 0; + if(level && read_delegate_phase_ == ReadDelegatePhase::WaitingForZero) return; + + // Deal with a transition out of waiting-for-zero mode by seeding time left + // in bit at half a bit. + if(read_delegate_phase_ == ReadDelegatePhase::WaitingForZero) { + time_left_in_bit_ = read_delegate_bit_length_; + time_left_in_bit_.clock_rate <<= 1; + read_delegate_phase_ = ReadDelegatePhase::Serialising; + } + + // Forward as many bits as occur. + Storage::Time time_left(cycles_to_forward, clock_rate_); + const int bit = level ? 1 : 0; + int bits = 0; + while(time_left >= time_left_in_bit_) { + ++bits; + if(!read_delegate_->serial_line_did_produce_bit(this, bit)) { + read_delegate_phase_ = ReadDelegatePhase::WaitingForZero; + } + + time_left -= time_left_in_bit_; + time_left_in_bit_ = read_delegate_bit_length_; + } + time_left_in_bit_ -= time_left; +} diff --git a/Components/SerialPort/SerialPort.hpp b/Components/SerialPort/SerialPort.hpp index d4a2ac98a..611ace1dd 100644 --- a/Components/SerialPort/SerialPort.hpp +++ b/Components/SerialPort/SerialPort.hpp @@ -48,16 +48,22 @@ class Line { /// Eliminates all future write states, leaving the output at whatever it is now. void reset_writing(); - /// Applies all pending write changes instantly. - void flush_writing(); - /// @returns The instantaneous level of this line. bool read(); struct ReadDelegate { - virtual void serial_line_did_change_output(Line *line, Storage::Time time_since_last_change, bool old_level) = 0; + virtual bool serial_line_did_produce_bit(Line *line, int bit) = 0; }; - void set_read_delegate(ReadDelegate *delegate); + /*! + Sets a read delegate, which will receive samples of the output level every + @c bit_lengths of a second apart subject to a state machine: + + * initially no bits will be delivered; + * when a zero level is first detected, the line will wait half a bit's length, then start + sampling at single-bit intervals, passing each bit to the delegate while it returns @c true; + * as soon as the delegate returns @c false, the line will return to the initial state. + */ + void set_read_delegate(ReadDelegate *delegate, Storage::Time bit_length); private: struct Event { @@ -72,7 +78,14 @@ class Line { int clock_rate_ = 0; ReadDelegate *read_delegate_ = nullptr; + Storage::Time read_delegate_bit_length_, time_left_in_bit_; int write_cycles_since_delegate_call_ = 0; + enum class ReadDelegatePhase { + WaitingForZero, + Serialising + } read_delegate_phase_ = ReadDelegatePhase::WaitingForZero; + + void update_delegate(bool level); }; /*! diff --git a/Machines/AtariST/AtariST.cpp b/Machines/AtariST/AtariST.cpp index a5ccfe9c8..0c783541b 100644 --- a/Machines/AtariST/AtariST.cpp +++ b/Machines/AtariST/AtariST.cpp @@ -39,33 +39,18 @@ class IntelligentKeyboard: public Serial::Line::ReadDelegate { public: IntelligentKeyboard(Serial::Line &input, Serial::Line &output) : output_line_(output) { - input.set_read_delegate(this); + input.set_read_delegate(this, Storage::Time(2, 15625)); } - void serial_line_did_change_output(Serial::Line *, Storage::Time time_since_last_change, bool old_level) final { - // Figure out how many bits have passed. TODO: in fixed point? - const float number_of_bits = time_since_last_change.get() * 7812.5f + bit_offset_; - bit_offset_ = fmodf(number_of_bits, 1.0f); - printf("Changed from %d after %d bits\n", old_level, int(number_of_bits)); - - int bits_remaining = int(number_of_bits); - while(bits_remaining--) { - if(!bit_count_) { - // Check for a potential start bit. - if(!old_level) { - bit_count_ = 9; - command_ = 0; - } - } else { - command_ >>= 1; - command_ |= old_level ? 0x200 : 0x000; - --bit_count_; - - if(!bit_count_) { - LOG("[IKBD] Should perform " << PADHEX(2) << ((command_ >> 1) & 0xff)); - } - } + bool serial_line_did_produce_bit(Serial::Line *, int bit) final { + command_ = (command_ >> 1) | (bit << 9); + bit_count_ = (bit_count_ + 1) % 10; + if(!bit_count_) { + LOG("[IKBD] Should perform " << PADHEX(2) << ((command_ >> 1) & 0xff)); + command_ = 0; + return false; } + return true; } private: