mirror of
https://github.com/TomHarte/CLK.git
synced 2025-02-05 21:32:55 +00:00
Starts to transfer serial line decoding logic into the line itself.
This commit is contained in:
parent
ab50f17d87
commit
9ab49065cd
@ -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 {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
/*!
|
||||
|
@ -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:
|
||||
|
Loading…
x
Reference in New Issue
Block a user