1
0
mirror of https://github.com/TomHarte/CLK.git synced 2025-02-20 14:29:11 +00:00

Switches the Electron to JustInTimeActor video.

Also reorders template parameters; I think that specifying a different time base is likely to be more common than using a divider.
This commit is contained in:
Thomas Harte 2021-04-04 17:33:49 -04:00
parent 9ff392279a
commit d77ddaf4fa
8 changed files with 69 additions and 78 deletions

View File

@ -32,7 +32,7 @@
TODO: incorporate and codify AsyncJustInTimeActor. TODO: incorporate and codify AsyncJustInTimeActor.
*/ */
template <class T, int multiplier = 1, int divider = 1, class LocalTimeScale = HalfCycles> class JustInTimeActor: template <class T, class LocalTimeScale = HalfCycles, int multiplier = 1, int divider = 1> class JustInTimeActor:
public ClockingHint::Observer { public ClockingHint::Observer {
private: private:
/*! /*!
@ -47,7 +47,7 @@ template <class T, int multiplier = 1, int divider = 1, class LocalTimeScale = H
*/ */
class SequencePointAwareDeleter { class SequencePointAwareDeleter {
public: public:
explicit SequencePointAwareDeleter(JustInTimeActor<T, multiplier, divider, LocalTimeScale> *actor) noexcept explicit SequencePointAwareDeleter(JustInTimeActor<T, LocalTimeScale, multiplier, divider> *actor) noexcept
: actor_(actor) {} : actor_(actor) {}
forceinline void operator ()(const T *const) const { forceinline void operator ()(const T *const) const {
@ -57,7 +57,7 @@ template <class T, int multiplier = 1, int divider = 1, class LocalTimeScale = H
} }
private: private:
JustInTimeActor<T, multiplier, divider, LocalTimeScale> *const actor_; JustInTimeActor<T, LocalTimeScale, multiplier, divider> *const actor_;
}; };
// This block of SFINAE determines whether objects of type T accepts Cycles or HalfCycles. // This block of SFINAE determines whether objects of type T accepts Cycles or HalfCycles.
@ -126,7 +126,7 @@ template <class T, int multiplier = 1, int divider = 1, class LocalTimeScale = H
/// ///
/// Despite being const, this will flush the object and, if relevant, update the next sequence point. /// Despite being const, this will flush the object and, if relevant, update the next sequence point.
[[nodiscard]] forceinline auto operator -> () const { [[nodiscard]] forceinline auto operator -> () const {
auto non_const_this = const_cast<JustInTimeActor<T, multiplier, divider, LocalTimeScale> *>(this); auto non_const_this = const_cast<JustInTimeActor<T, LocalTimeScale, multiplier, divider> *>(this);
non_const_this->flush(); non_const_this->flush();
return std::unique_ptr<const T, SequencePointAwareDeleter>(&object_, SequencePointAwareDeleter(non_const_this)); return std::unique_ptr<const T, SequencePointAwareDeleter>(&object_, SequencePointAwareDeleter(non_const_this));
} }

View File

@ -1093,10 +1093,10 @@ class ConcreteMachine:
// MARK: - Other components. // MARK: - Other components.
Apple::Clock::ParallelClock clock_; Apple::Clock::ParallelClock clock_;
JustInTimeActor<Apple::IIgs::Video::Video, 1, 2, Cycles> video_; // i.e. run video at 7Mhz. JustInTimeActor<Apple::IIgs::Video::Video, Cycles, 1, 2> video_; // i.e. run video at 7Mhz.
JustInTimeActor<Apple::IIgs::ADB::GLU, 1, 4, Cycles> adb_glu_; // i.e. 3,579,545Mhz. JustInTimeActor<Apple::IIgs::ADB::GLU, Cycles, 1, 4> adb_glu_; // i.e. 3,579,545Mhz.
Zilog::SCC::z8530 scc_; Zilog::SCC::z8530 scc_;
JustInTimeActor<Apple::IWM, 1, 2, Cycles> iwm_; JustInTimeActor<Apple::IWM, Cycles, 1, 2> iwm_;
Cycles cycles_since_clock_tick_; Cycles cycles_since_clock_tick_;
Apple::Macintosh::DoubleDensityDrive drives35_[2]; Apple::Macintosh::DoubleDensityDrive drives35_[2];
Apple::Disk::DiskIIDrive drives525_[2]; Apple::Disk::DiskIIDrive drives525_[2];

View File

@ -479,9 +479,9 @@ class ConcreteMachine:
JustInTimeActor<Video> video_; JustInTimeActor<Video> video_;
// The MFP runs at 819200/2673749ths of the CPU clock rate. // The MFP runs at 819200/2673749ths of the CPU clock rate.
JustInTimeActor<Motorola::MFP68901::MFP68901, 819200, 2673749> mfp_; JustInTimeActor<Motorola::MFP68901::MFP68901, HalfCycles, 819200, 2673749> mfp_;
JustInTimeActor<Motorola::ACIA::ACIA, 16> keyboard_acia_; JustInTimeActor<Motorola::ACIA::ACIA, HalfCycles, 16> keyboard_acia_;
JustInTimeActor<Motorola::ACIA::ACIA, 16> midi_acia_; JustInTimeActor<Motorola::ACIA::ACIA, HalfCycles, 16> midi_acia_;
Concurrency::DeferringAsyncTaskQueue audio_queue_; Concurrency::DeferringAsyncTaskQueue audio_queue_;
GI::AY38910::AY38910<false> ay_; GI::AY38910::AY38910<false> ay_;

View File

@ -384,7 +384,7 @@ class ConcreteMachine:
} }
CPU::Z80::Processor<ConcreteMachine, false, false> z80_; CPU::Z80::Processor<ConcreteMachine, false, false> z80_;
JustInTimeActor<TI::TMS::TMS9918, 1, 1, HalfCycles> vdp_; JustInTimeActor<TI::TMS::TMS9918> vdp_;
Concurrency::DeferringAsyncTaskQueue audio_queue_; Concurrency::DeferringAsyncTaskQueue audio_queue_;
TI::SN76489 sn76489_; TI::SN76489 sn76489_;

View File

@ -25,6 +25,8 @@
#include "../Utility/Typer.hpp" #include "../Utility/Typer.hpp"
#include "../../Analyser/Static/Acorn/Target.hpp" #include "../../Analyser/Static/Acorn/Target.hpp"
#include "../../ClockReceiver/JustInTime.hpp"
#include "Interrupts.hpp" #include "Interrupts.hpp"
#include "Keyboard.hpp" #include "Keyboard.hpp"
#include "Plus3.hpp" #include "Plus3.hpp"
@ -54,7 +56,7 @@ template <bool has_scsi_bus> class ConcreteMachine:
scsi_bus_(4'000'000), scsi_bus_(4'000'000),
hard_drive_(scsi_bus_, 0), hard_drive_(scsi_bus_, 0),
scsi_device_(scsi_bus_.add_device()), scsi_device_(scsi_bus_.add_device()),
video_output_(ram_), video_(ram_),
sound_generator_(audio_queue_), sound_generator_(audio_queue_),
speaker_(sound_generator_) { speaker_(sound_generator_) {
memset(key_states_, 0, sizeof(key_states_)); memset(key_states_, 0, sizeof(key_states_));
@ -230,13 +232,15 @@ template <bool has_scsi_bus> class ConcreteMachine:
if(isReadOperation(operation)) { if(isReadOperation(operation)) {
*value = ram_[address]; *value = ram_[address];
} else { } else {
if(address >= video_access_range_.low_address && address <= video_access_range_.high_address) update_display(); if(address >= video_access_range_.low_address && address <= video_access_range_.high_address) {
video_.flush();
}
ram_[address] = *value; ram_[address] = *value;
} }
// for the entire frame, RAM is accessible only on odd cycles; in modes below 4 // For the entire frame, RAM is accessible only on odd cycles; in modes below 4
// it's also accessible only outside of the pixel regions // it's also accessible only outside of the pixel regions.
cycles += video_output_.get_cycles_until_next_ram_availability(int(cycles_since_display_update_.as_integral()) + 1); cycles += video_.last_valid()->get_cycles_until_next_ram_availability(video_.time_since_flush().template as<int>() + 1);
} else { } else {
switch(address & 0xff0f) { switch(address & 0xff0f) {
case 0xfe00: case 0xfe00:
@ -274,10 +278,8 @@ template <bool has_scsi_bus> class ConcreteMachine:
case 0xfe08: case 0xfe09: case 0xfe0a: case 0xfe0b: case 0xfe08: case 0xfe09: case 0xfe0a: case 0xfe0b:
case 0xfe0c: case 0xfe0d: case 0xfe0e: case 0xfe0f: case 0xfe0c: case 0xfe0d: case 0xfe0e: case 0xfe0f:
if(!isReadOperation(operation)) { if(!isReadOperation(operation)) {
update_display(); video_->write(address, *value);
video_output_.write(address, *value); video_access_range_ = video_.last_valid()->get_memory_access_range();
video_access_range_ = video_output_.get_memory_access_range();
queue_next_display_interrupt();
} }
break; break;
case 0xfe04: case 0xfe04:
@ -483,18 +485,14 @@ template <bool has_scsi_bus> class ConcreteMachine:
} }
} }
cycles_since_display_update_ += Cycles(int(cycles)); if(video_ += Cycles(int(cycles))) {
signal_interrupt(video_.last_valid()->get_interrupts());
}
cycles_since_audio_update_ += Cycles(int(cycles)); cycles_since_audio_update_ += Cycles(int(cycles));
if(cycles_since_audio_update_ > Cycles(16384)) update_audio(); if(cycles_since_audio_update_ > Cycles(16384)) update_audio();
tape_.run_for(Cycles(int(cycles))); tape_.run_for(Cycles(int(cycles)));
cycles_until_display_interrupt_ -= cycles;
if(cycles_until_display_interrupt_ < 0) {
signal_interrupt(next_display_interrupt_);
update_display();
queue_next_display_interrupt();
}
if(typer_) typer_->run_for(Cycles(int(cycles))); if(typer_) typer_->run_for(Cycles(int(cycles)));
if(plus3_) plus3_->run_for(Cycles(4*int(cycles))); if(plus3_) plus3_->run_for(Cycles(4*int(cycles)));
if(shift_restart_counter_) { if(shift_restart_counter_) {
@ -517,25 +515,25 @@ template <bool has_scsi_bus> class ConcreteMachine:
} }
forceinline void flush() { forceinline void flush() {
update_display(); video_.flush();
update_audio(); update_audio();
audio_queue_.perform(); audio_queue_.perform();
} }
void set_scan_target(Outputs::Display::ScanTarget *scan_target) final { void set_scan_target(Outputs::Display::ScanTarget *scan_target) final {
video_output_.set_scan_target(scan_target); video_->set_scan_target(scan_target);
} }
Outputs::Display::ScanStatus get_scaled_scan_status() const final { Outputs::Display::ScanStatus get_scaled_scan_status() const final {
return video_output_.get_scaled_scan_status(); return video_->get_scaled_scan_status();
} }
void set_display_type(Outputs::Display::DisplayType display_type) final { void set_display_type(Outputs::Display::DisplayType display_type) final {
video_output_.set_display_type(display_type); video_->set_display_type(display_type);
} }
Outputs::Display::DisplayType get_display_type() const final { Outputs::Display::DisplayType get_display_type() const final {
return video_output_.get_display_type(); return video_->get_display_type();
} }
Outputs::Speaker::Speaker *get_speaker() final { Outputs::Speaker::Speaker *get_speaker() final {
@ -693,18 +691,6 @@ template <bool has_scsi_bus> class ConcreteMachine:
} }
// MARK: - Work deferral updates. // MARK: - Work deferral updates.
inline void update_display() {
if(cycles_since_display_update_ > 0) {
video_output_.run_for(cycles_since_display_update_.flush<Cycles>());
}
}
inline void queue_next_display_interrupt() {
VideoOutput::Interrupt next_interrupt = video_output_.get_next_interrupt();
cycles_until_display_interrupt_ = next_interrupt.cycles;
next_display_interrupt_ = next_interrupt.interrupt;
}
inline void update_audio() { inline void update_audio() {
speaker_.run_for(audio_queue_, cycles_since_audio_update_.divide(Cycles(SoundGenerator::clock_rate_divider))); speaker_.run_for(audio_queue_, cycles_since_audio_update_.divide(Cycles(SoundGenerator::clock_rate_divider)));
} }
@ -754,10 +740,7 @@ template <bool has_scsi_bus> class ConcreteMachine:
Electron::KeyboardMapper keyboard_mapper_; Electron::KeyboardMapper keyboard_mapper_;
// Counters related to simultaneous subsystems // Counters related to simultaneous subsystems
Cycles cycles_since_display_update_ = 0;
Cycles cycles_since_audio_update_ = 0; Cycles cycles_since_audio_update_ = 0;
int cycles_until_display_interrupt_ = 0;
Interrupt next_display_interrupt_ = Interrupt::RealTimeClock;
VideoOutput::Range video_access_range_ = {0, 0xffff}; VideoOutput::Range video_access_range_ = {0, 0xffff};
// Tape // Tape
@ -794,7 +777,7 @@ template <bool has_scsi_bus> class ConcreteMachine:
} }
// Outputs // Outputs
VideoOutput video_output_; JustInTimeActor<VideoOutput, Cycles> video_;
Concurrency::DeferringAsyncTaskQueue audio_queue_; Concurrency::DeferringAsyncTaskQueue audio_queue_;
SoundGenerator sound_generator_; SoundGenerator sound_generator_;

View File

@ -234,7 +234,23 @@ void VideoOutput::output_pixels(int number_of_cycles) {
void VideoOutput::run_for(const Cycles cycles) { void VideoOutput::run_for(const Cycles cycles) {
int number_of_cycles = int(cycles.as_integral()); int number_of_cycles = int(cycles.as_integral());
const auto start_position = output_position_;
output_position_ = (output_position_ + number_of_cycles) % cycles_per_frame; output_position_ = (output_position_ + number_of_cycles) % cycles_per_frame;
if(
(start_position < real_time_clock_interrupt_1 && output_position_ >= real_time_clock_interrupt_1) ||
(start_position < real_time_clock_interrupt_2 && output_position_ >= real_time_clock_interrupt_2)
) {
interrupts_ = Electron::Interrupt(interrupts_ | Electron::Interrupt::RealTimeClock);
}
if(
(start_position < display_end_interrupt_1 && output_position_ >= display_end_interrupt_1) ||
(start_position < display_end_interrupt_2 && output_position_ >= display_end_interrupt_2)
) {
interrupts_ = Electron::Interrupt(interrupts_ | Electron::Interrupt::DisplayEnd);
}
while(number_of_cycles) { while(number_of_cycles) {
int draw_action_length = screen_map_[screen_map_pointer_].length; int draw_action_length = screen_map_[screen_map_pointer_].length;
int time_left_in_action = std::min(number_of_cycles, draw_action_length - cycles_into_draw_action_); int time_left_in_action = std::min(number_of_cycles, draw_action_length - cycles_into_draw_action_);
@ -354,36 +370,30 @@ void VideoOutput::setup_base_address() {
// MARK: - Interrupts // MARK: - Interrupts
VideoOutput::Interrupt VideoOutput::get_next_interrupt() { Cycles VideoOutput::get_next_sequence_point() {
VideoOutput::Interrupt interrupt;
if(output_position_ < real_time_clock_interrupt_1) { if(output_position_ < real_time_clock_interrupt_1) {
interrupt.cycles = real_time_clock_interrupt_1 - output_position_; return real_time_clock_interrupt_1 - output_position_;
interrupt.interrupt = RealTimeClock;
return interrupt;
} }
if(output_position_ < display_end_interrupt_1) { if(output_position_ < display_end_interrupt_1) {
interrupt.cycles = display_end_interrupt_1 - output_position_; return display_end_interrupt_1 - output_position_;
interrupt.interrupt = DisplayEnd;
return interrupt;
} }
if(output_position_ < real_time_clock_interrupt_2) { if(output_position_ < real_time_clock_interrupt_2) {
interrupt.cycles = real_time_clock_interrupt_2 - output_position_; return real_time_clock_interrupt_2 - output_position_;
interrupt.interrupt = RealTimeClock;
return interrupt;
} }
if(output_position_ < display_end_interrupt_2) { if(output_position_ < display_end_interrupt_2) {
interrupt.cycles = display_end_interrupt_2 - output_position_; return display_end_interrupt_2 - output_position_;
interrupt.interrupt = DisplayEnd;
return interrupt;
} }
interrupt.cycles = real_time_clock_interrupt_1 + cycles_per_frame - output_position_; return real_time_clock_interrupt_1 + cycles_per_frame - output_position_;
interrupt.interrupt = RealTimeClock; }
return interrupt;
Electron::Interrupt VideoOutput::get_interrupts() {
const auto interrupts = interrupts_;
interrupts_ = Electron::Interrupt(0);
return interrupts;
} }
// MARK: - RAM timing and access information // MARK: - RAM timing and access information

View File

@ -54,15 +54,6 @@ class VideoOutput {
*/ */
void write(int address, uint8_t value); void write(int address, uint8_t value);
/*!
Describes an interrupt the video hardware will generate by its identity and scheduling time.
*/
struct Interrupt {
/// The interrupt that will be signalled.
Electron::Interrupt interrupt;
/// The number of cycles until it is signalled.
int cycles;
};
/*! /*!
@returns the next interrupt that should be generated as a result of the video hardware. @returns the next interrupt that should be generated as a result of the video hardware.
The time until signalling returned is the number of cycles after the final one triggered The time until signalling returned is the number of cycles after the final one triggered
@ -70,7 +61,12 @@ class VideoOutput {
This result may be mutated by calls to @c write. This result may be mutated by calls to @c write.
*/ */
Interrupt get_next_interrupt(); Cycles get_next_sequence_point();
/*!
@returns a bit mask of all interrupts that have been triggered since the last call to get_interrupt().
*/
Electron::Interrupt get_interrupts();
/*! /*!
@returns the number of cycles after (final cycle of last run_for batch + @c from_time) @returns the number of cycles after (final cycle of last run_for batch + @c from_time)
@ -136,6 +132,8 @@ class VideoOutput {
void emplace_pixel_line(); void emplace_pixel_line();
std::size_t screen_map_pointer_ = 0; std::size_t screen_map_pointer_ = 0;
int cycles_into_draw_action_ = 0; int cycles_into_draw_action_ = 0;
Electron::Interrupt interrupts_ = Electron::Interrupt(0);
}; };
} }

View File

@ -664,7 +664,7 @@ template<Model model> class ConcreteMachine:
} }
// MARK: - Disc. // MARK: - Disc.
JustInTimeActor<Amstrad::FDC, 1, 1, Cycles> fdc_; JustInTimeActor<Amstrad::FDC, Cycles> fdc_;
// MARK: - Automatic startup. // MARK: - Automatic startup.
Cycles duration_to_press_enter_; Cycles duration_to_press_enter_;