mirror of
https://github.com/TomHarte/CLK.git
synced 2025-02-19 23:29:05 +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.
|
/// Flushes all accumulated time.
|
||||||
void flush() {
|
void flush() {
|
||||||
if(!is_flushed_) {
|
if(!is_flushed_) {
|
||||||
|
is_flushed_ = true;
|
||||||
if(divider == 1) {
|
if(divider == 1) {
|
||||||
object_.run_for(time_since_update_.template flush<TargetTimeScale>());
|
object_.run_for(time_since_update_.template flush<TargetTimeScale>());
|
||||||
} else {
|
} else {
|
||||||
object_.run_for(time_since_update_.template divide<TargetTimeScale>(LocalTimeScale(divider)));
|
object_.run_for(time_since_update_.template divide<TargetTimeScale>(LocalTimeScale(divider)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is_flushed_ = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -97,9 +97,13 @@ void ACIA::write(int address, uint8_t value) {
|
|||||||
void ACIA::run_for(HalfCycles length) {
|
void ACIA::run_for(HalfCycles length) {
|
||||||
// Transmission.
|
// Transmission.
|
||||||
const int transmit_advance = length.as_int();
|
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(transmit_advance >= write_data_time_remaining) {
|
||||||
if(next_transmission_ != NoValue) {
|
if(next_transmission_ != NoValue) {
|
||||||
transmit.advance_writer(write_data_time_remaining);
|
transmit.advance_writer(write_data_time_remaining);
|
||||||
@ -145,6 +149,9 @@ void ACIA::consider_transmission() {
|
|||||||
|
|
||||||
// Output all that.
|
// Output all that.
|
||||||
const int total_bits = 1 + data_bits_ + stop_bits_ + (parity_ != Parity::None);
|
const int total_bits = 1 + data_bits_ + stop_bits_ + (parity_ != Parity::None);
|
||||||
|
if(!next_transmission_) {
|
||||||
|
printf("");
|
||||||
|
}
|
||||||
transmit.write(divider_ * 2, total_bits, transmission);
|
transmit.write(divider_ * 2, total_bits, transmission);
|
||||||
printf("Transmitted %02x [%03x]\n", next_transmission_, transmission);
|
printf("Transmitted %02x [%03x]\n", next_transmission_, transmission);
|
||||||
|
|
||||||
@ -154,7 +161,14 @@ void ACIA::consider_transmission() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ClockingHint::Preference ACIA::preferred_clocking() {
|
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 {
|
bool ACIA::get_interrupt_line() const {
|
||||||
|
@ -62,6 +62,9 @@ class ACIA: public ClockingHint::Source {
|
|||||||
Serial::Line transmit;
|
Serial::Line transmit;
|
||||||
Serial::Line request_to_send;
|
Serial::Line request_to_send;
|
||||||
|
|
||||||
|
// ClockingHint::Source.
|
||||||
|
ClockingHint::Preference preferred_clocking() final;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int divider_ = 1;
|
int divider_ = 1;
|
||||||
enum class Parity {
|
enum class Parity {
|
||||||
@ -80,8 +83,6 @@ class ACIA: public ClockingHint::Source {
|
|||||||
|
|
||||||
bool interrupt_request_ = false;
|
bool interrupt_request_ = false;
|
||||||
|
|
||||||
ClockingHint::Preference preferred_clocking() final;
|
|
||||||
|
|
||||||
HalfCycles transmit_clock_rate_;
|
HalfCycles transmit_clock_rate_;
|
||||||
HalfCycles receive_clock_rate_;
|
HalfCycles receive_clock_rate_;
|
||||||
};
|
};
|
||||||
|
@ -18,10 +18,16 @@ void Line::advance_writer(int cycles) {
|
|||||||
remaining_delays_ = std::max(remaining_delays_ - cycles, 0);
|
remaining_delays_ = std::max(remaining_delays_ - cycles, 0);
|
||||||
if(events_.empty()) {
|
if(events_.empty()) {
|
||||||
write_cycles_since_delegate_call_ += cycles;
|
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 {
|
} else {
|
||||||
while(!events_.empty()) {
|
while(!events_.empty()) {
|
||||||
if(events_.front().delay < cycles) {
|
if(events_.front().delay <= cycles) {
|
||||||
cycles -= events_.front().delay;
|
cycles -= events_.front().delay;
|
||||||
write_cycles_since_delegate_call_ += events_.front().delay;
|
write_cycles_since_delegate_call_ += events_.front().delay;
|
||||||
const auto old_level = level_;
|
const auto old_level = level_;
|
||||||
@ -36,6 +42,12 @@ void Line::advance_writer(int cycles) {
|
|||||||
if(old_level != level_) {
|
if(old_level != level_) {
|
||||||
update_delegate(old_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 {
|
} else {
|
||||||
events_.front().delay -= cycles;
|
events_.front().delay -= cycles;
|
||||||
write_cycles_since_delegate_call_ += cycles;
|
write_cycles_since_delegate_call_ += cycles;
|
||||||
@ -51,6 +63,7 @@ void Line::write(bool level) {
|
|||||||
events_.back().type = level ? Event::SetHigh : Event::SetLow;
|
events_.back().type = level ? Event::SetHigh : Event::SetLow;
|
||||||
} else {
|
} else {
|
||||||
level_ = level;
|
level_ = level;
|
||||||
|
transmission_extra_ = minimum_write_cycles_for_read_delegate_bit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,6 +85,10 @@ int Line::write_data_time_remaining() {
|
|||||||
return remaining_delays_;
|
return remaining_delays_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int Line::transmission_data_time_remaining() {
|
||||||
|
return remaining_delays_ + transmission_extra_;
|
||||||
|
}
|
||||||
|
|
||||||
void Line::reset_writing() {
|
void Line::reset_writing() {
|
||||||
remaining_delays_ = 0;
|
remaining_delays_ = 0;
|
||||||
events_.clear();
|
events_.clear();
|
||||||
@ -119,3 +136,8 @@ void Line::update_delegate(bool level) {
|
|||||||
}
|
}
|
||||||
time_left_in_bit_ -= time_left;
|
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.
|
/// @returns the number of cycles until currently enqueued write data is exhausted.
|
||||||
int write_data_time_remaining();
|
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.
|
/// Eliminates all future write states, leaving the output at whatever it is now.
|
||||||
void reset_writing();
|
void reset_writing();
|
||||||
|
|
||||||
@ -74,6 +78,7 @@ class Line {
|
|||||||
};
|
};
|
||||||
std::vector<Event> events_;
|
std::vector<Event> events_;
|
||||||
int remaining_delays_ = 0;
|
int remaining_delays_ = 0;
|
||||||
|
int transmission_extra_ = 0;
|
||||||
bool level_ = false;
|
bool level_ = false;
|
||||||
int clock_rate_ = 0;
|
int clock_rate_ = 0;
|
||||||
|
|
||||||
@ -86,6 +91,7 @@ class Line {
|
|||||||
} read_delegate_phase_ = ReadDelegatePhase::WaitingForZero;
|
} read_delegate_phase_ = ReadDelegatePhase::WaitingForZero;
|
||||||
|
|
||||||
void update_delegate(bool level);
|
void update_delegate(bool level);
|
||||||
|
int minimum_write_cycles_for_read_delegate_bit();
|
||||||
};
|
};
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
@ -64,7 +64,8 @@ using Target = Analyser::Static::Target;
|
|||||||
class ConcreteMachine:
|
class ConcreteMachine:
|
||||||
public Atari::ST::Machine,
|
public Atari::ST::Machine,
|
||||||
public CPU::MC68000::BusHandler,
|
public CPU::MC68000::BusHandler,
|
||||||
public CRTMachine::Machine {
|
public CRTMachine::Machine,
|
||||||
|
public ClockingHint::Observer {
|
||||||
public:
|
public:
|
||||||
ConcreteMachine(const Target &target, const ROMMachine::ROMFetcher &rom_fetcher) :
|
ConcreteMachine(const Target &target, const ROMMachine::ROMFetcher &rom_fetcher) :
|
||||||
mc68000_(*this),
|
mc68000_(*this),
|
||||||
@ -99,6 +100,9 @@ class ConcreteMachine:
|
|||||||
memory_map_[0xfa] = memory_map_[0xfb] = BusDevice::Cartridge;
|
memory_map_[0xfa] = memory_map_[0xfb] = BusDevice::Cartridge;
|
||||||
|
|
||||||
memory_map_[0xff] = BusDevice::IO;
|
memory_map_[0xff] = BusDevice::IO;
|
||||||
|
|
||||||
|
midi_acia_->set_clocking_hint_observer(this);
|
||||||
|
keyboard_acia_->set_clocking_hint_observer(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
~ConcreteMachine() {
|
~ConcreteMachine() {
|
||||||
@ -377,8 +381,13 @@ class ConcreteMachine:
|
|||||||
forceinline void advance_time(HalfCycles length) {
|
forceinline void advance_time(HalfCycles length) {
|
||||||
cycles_since_audio_update_ += length;
|
cycles_since_audio_update_ += length;
|
||||||
mfp_ += length;
|
mfp_ += length;
|
||||||
|
|
||||||
keyboard_acia_ += length;
|
keyboard_acia_ += length;
|
||||||
midi_acia_ += length;
|
midi_acia_ += length;
|
||||||
|
if(!may_defer_acias_) {
|
||||||
|
keyboard_acia_.flush();
|
||||||
|
midi_acia_.flush();
|
||||||
|
}
|
||||||
|
|
||||||
while(length >= cycles_until_video_event_) {
|
while(length >= cycles_until_video_event_) {
|
||||||
length -= cycles_until_video_event_;
|
length -= cycles_until_video_event_;
|
||||||
@ -419,6 +428,15 @@ class ConcreteMachine:
|
|||||||
MostlyRAM, RAM, ROM, Cartridge, IO, Unassigned
|
MostlyRAM, RAM, ROM, Cartridge, IO, Unassigned
|
||||||
};
|
};
|
||||||
BusDevice memory_map_[256];
|
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