1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-07-04 18:29:40 +00:00

Starts to transfer serial line decoding logic into the line itself.

This commit is contained in:
Thomas Harte 2019-10-17 23:34:39 -04:00
parent ab50f17d87
commit 9ab49065cd
4 changed files with 66 additions and 59 deletions

View File

@ -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 {

View File

@ -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;
}

View File

@ -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);
};
/*!

View File

@ -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<float>() * 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: