mirror of
https://github.com/TomHarte/CLK.git
synced 2025-02-16 18:30:32 +00:00
Ensures good serial line and ACIA behaviour.
Next stop: having the intelligent keyboard react.
This commit is contained in:
parent
313aaa8f95
commit
cf07982a9b
@ -46,13 +46,13 @@ template <class T, int divider = 1, class LocalTimeScale = HalfCycles, class Tar
|
||||
/// Flushes all accumulated time.
|
||||
void flush() {
|
||||
if(!is_flushed_) {
|
||||
is_flushed_ = true;
|
||||
if(divider == 1) {
|
||||
object_.run_for(time_since_update_.template flush<TargetTimeScale>());
|
||||
} else {
|
||||
object_.run_for(time_since_update_.template divide<TargetTimeScale>(LocalTimeScale(divider)));
|
||||
}
|
||||
}
|
||||
is_flushed_ = true;
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -97,9 +97,13 @@ void ACIA::write(int address, uint8_t value) {
|
||||
void ACIA::run_for(HalfCycles length) {
|
||||
// Transmission.
|
||||
const int transmit_advance = length.as_int();
|
||||
const auto write_data_time_remaining = transmit.write_data_time_remaining();
|
||||
|
||||
if(write_data_time_remaining) {
|
||||
if(transmit.transmission_data_time_remaining()) {
|
||||
const auto write_data_time_remaining = transmit.write_data_time_remaining();
|
||||
|
||||
// There's at most one further byte available to enqueue, so a single 'if'
|
||||
// rather than a 'while' is correct here. It's the responsibilit of the caller
|
||||
// to ensure run_for lengths are appropriate for longer sequences.
|
||||
if(transmit_advance >= write_data_time_remaining) {
|
||||
if(next_transmission_ != NoValue) {
|
||||
transmit.advance_writer(write_data_time_remaining);
|
||||
@ -145,6 +149,9 @@ void ACIA::consider_transmission() {
|
||||
|
||||
// Output all that.
|
||||
const int total_bits = 1 + data_bits_ + stop_bits_ + (parity_ != Parity::None);
|
||||
if(!next_transmission_) {
|
||||
printf("");
|
||||
}
|
||||
transmit.write(divider_ * 2, total_bits, transmission);
|
||||
printf("Transmitted %02x [%03x]\n", next_transmission_, transmission);
|
||||
|
||||
@ -154,7 +161,14 @@ void ACIA::consider_transmission() {
|
||||
}
|
||||
|
||||
ClockingHint::Preference ACIA::preferred_clocking() {
|
||||
return (transmit.write_data_time_remaining() > 0) ? ClockingHint::Preference::JustInTime : ClockingHint::Preference::None;
|
||||
// Real-time clocking is required if a transmission is ongoing; this is a courtesy for whomever
|
||||
// is on the receiving end.
|
||||
if(transmit.transmission_data_time_remaining() > 0) return ClockingHint::Preference::RealTime;
|
||||
|
||||
// TODO: real-time clocking if a process of receiving is ongoing.
|
||||
|
||||
// No clocking required then.
|
||||
return ClockingHint::Preference::None;
|
||||
}
|
||||
|
||||
bool ACIA::get_interrupt_line() const {
|
||||
|
@ -62,6 +62,9 @@ class ACIA: public ClockingHint::Source {
|
||||
Serial::Line transmit;
|
||||
Serial::Line request_to_send;
|
||||
|
||||
// ClockingHint::Source.
|
||||
ClockingHint::Preference preferred_clocking() final;
|
||||
|
||||
private:
|
||||
int divider_ = 1;
|
||||
enum class Parity {
|
||||
@ -80,8 +83,6 @@ class ACIA: public ClockingHint::Source {
|
||||
|
||||
bool interrupt_request_ = false;
|
||||
|
||||
ClockingHint::Preference preferred_clocking() final;
|
||||
|
||||
HalfCycles transmit_clock_rate_;
|
||||
HalfCycles receive_clock_rate_;
|
||||
};
|
||||
|
@ -18,10 +18,16 @@ void Line::advance_writer(int cycles) {
|
||||
remaining_delays_ = std::max(remaining_delays_ - cycles, 0);
|
||||
if(events_.empty()) {
|
||||
write_cycles_since_delegate_call_ += cycles;
|
||||
if(write_cycles_since_delegate_call_ > 256) update_delegate(level_);
|
||||
if(transmission_extra_) {
|
||||
transmission_extra_ -= cycles;
|
||||
if(transmission_extra_ <= 0) {
|
||||
transmission_extra_ = 0;
|
||||
update_delegate(level_);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
while(!events_.empty()) {
|
||||
if(events_.front().delay < cycles) {
|
||||
if(events_.front().delay <= cycles) {
|
||||
cycles -= events_.front().delay;
|
||||
write_cycles_since_delegate_call_ += events_.front().delay;
|
||||
const auto old_level = level_;
|
||||
@ -36,6 +42,12 @@ void Line::advance_writer(int cycles) {
|
||||
if(old_level != level_) {
|
||||
update_delegate(old_level);
|
||||
}
|
||||
|
||||
// Book enough extra time for the read delegate to be posted
|
||||
// the final bit if one is attached.
|
||||
if(events_.empty()) {
|
||||
transmission_extra_ = minimum_write_cycles_for_read_delegate_bit();
|
||||
}
|
||||
} else {
|
||||
events_.front().delay -= cycles;
|
||||
write_cycles_since_delegate_call_ += cycles;
|
||||
@ -51,6 +63,7 @@ void Line::write(bool level) {
|
||||
events_.back().type = level ? Event::SetHigh : Event::SetLow;
|
||||
} else {
|
||||
level_ = level;
|
||||
transmission_extra_ = minimum_write_cycles_for_read_delegate_bit();
|
||||
}
|
||||
}
|
||||
|
||||
@ -72,6 +85,10 @@ int Line::write_data_time_remaining() {
|
||||
return remaining_delays_;
|
||||
}
|
||||
|
||||
int Line::transmission_data_time_remaining() {
|
||||
return remaining_delays_ + transmission_extra_;
|
||||
}
|
||||
|
||||
void Line::reset_writing() {
|
||||
remaining_delays_ = 0;
|
||||
events_.clear();
|
||||
@ -119,3 +136,8 @@ void Line::update_delegate(bool level) {
|
||||
}
|
||||
time_left_in_bit_ -= time_left;
|
||||
}
|
||||
|
||||
int Line::minimum_write_cycles_for_read_delegate_bit() {
|
||||
if(!read_delegate_) return 0;
|
||||
return 1 + (read_delegate_bit_length_ * static_cast<unsigned int>(clock_rate_)).get<int>();
|
||||
}
|
||||
|
@ -45,6 +45,10 @@ class Line {
|
||||
/// @returns the number of cycles until currently enqueued write data is exhausted.
|
||||
int write_data_time_remaining();
|
||||
|
||||
/// @returns the number of cycles left until it is guaranteed that a passive reader
|
||||
/// has received all currently-enqueued bits.
|
||||
int transmission_data_time_remaining();
|
||||
|
||||
/// Eliminates all future write states, leaving the output at whatever it is now.
|
||||
void reset_writing();
|
||||
|
||||
@ -74,6 +78,7 @@ class Line {
|
||||
};
|
||||
std::vector<Event> events_;
|
||||
int remaining_delays_ = 0;
|
||||
int transmission_extra_ = 0;
|
||||
bool level_ = false;
|
||||
int clock_rate_ = 0;
|
||||
|
||||
@ -86,6 +91,7 @@ class Line {
|
||||
} read_delegate_phase_ = ReadDelegatePhase::WaitingForZero;
|
||||
|
||||
void update_delegate(bool level);
|
||||
int minimum_write_cycles_for_read_delegate_bit();
|
||||
};
|
||||
|
||||
/*!
|
||||
|
@ -64,7 +64,8 @@ using Target = Analyser::Static::Target;
|
||||
class ConcreteMachine:
|
||||
public Atari::ST::Machine,
|
||||
public CPU::MC68000::BusHandler,
|
||||
public CRTMachine::Machine {
|
||||
public CRTMachine::Machine,
|
||||
public ClockingHint::Observer {
|
||||
public:
|
||||
ConcreteMachine(const Target &target, const ROMMachine::ROMFetcher &rom_fetcher) :
|
||||
mc68000_(*this),
|
||||
@ -99,6 +100,9 @@ class ConcreteMachine:
|
||||
memory_map_[0xfa] = memory_map_[0xfb] = BusDevice::Cartridge;
|
||||
|
||||
memory_map_[0xff] = BusDevice::IO;
|
||||
|
||||
midi_acia_->set_clocking_hint_observer(this);
|
||||
keyboard_acia_->set_clocking_hint_observer(this);
|
||||
}
|
||||
|
||||
~ConcreteMachine() {
|
||||
@ -377,8 +381,13 @@ class ConcreteMachine:
|
||||
forceinline void advance_time(HalfCycles length) {
|
||||
cycles_since_audio_update_ += length;
|
||||
mfp_ += length;
|
||||
|
||||
keyboard_acia_ += length;
|
||||
midi_acia_ += length;
|
||||
if(!may_defer_acias_) {
|
||||
keyboard_acia_.flush();
|
||||
midi_acia_.flush();
|
||||
}
|
||||
|
||||
while(length >= cycles_until_video_event_) {
|
||||
length -= cycles_until_video_event_;
|
||||
@ -419,6 +428,15 @@ class ConcreteMachine:
|
||||
MostlyRAM, RAM, ROM, Cartridge, IO, Unassigned
|
||||
};
|
||||
BusDevice memory_map_[256];
|
||||
|
||||
bool may_defer_acias_ = true;
|
||||
void set_component_prefers_clocking(ClockingHint::Source *component, ClockingHint::Preference clocking) final {
|
||||
// This is being called by one of the components; avoid any time flushing here as that's
|
||||
// already dealt with (and, just to be absolutely sure, to avoid recursive mania).
|
||||
may_defer_acias_ =
|
||||
(keyboard_acia_.last_valid()->preferred_clocking() != ClockingHint::Preference::RealTime) &&
|
||||
(midi_acia_.last_valid()->preferred_clocking() != ClockingHint::Preference::RealTime);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user