1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-06-09 17:29:36 +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.
*/
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 {
private:
/*!
@ -47,7 +47,7 @@ template <class T, int multiplier = 1, int divider = 1, class LocalTimeScale = H
*/
class SequencePointAwareDeleter {
public:
explicit SequencePointAwareDeleter(JustInTimeActor<T, multiplier, divider, LocalTimeScale> *actor) noexcept
explicit SequencePointAwareDeleter(JustInTimeActor<T, LocalTimeScale, multiplier, divider> *actor) noexcept
: actor_(actor) {}
forceinline void operator ()(const T *const) const {
@ -57,7 +57,7 @@ template <class T, int multiplier = 1, int divider = 1, class LocalTimeScale = H
}
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.
@ -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.
[[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();
return std::unique_ptr<const T, SequencePointAwareDeleter>(&object_, SequencePointAwareDeleter(non_const_this));
}

View File

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

View File

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

View File

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

View File

@ -25,6 +25,8 @@
#include "../Utility/Typer.hpp"
#include "../../Analyser/Static/Acorn/Target.hpp"
#include "../../ClockReceiver/JustInTime.hpp"
#include "Interrupts.hpp"
#include "Keyboard.hpp"
#include "Plus3.hpp"
@ -54,7 +56,7 @@ template <bool has_scsi_bus> class ConcreteMachine:
scsi_bus_(4'000'000),
hard_drive_(scsi_bus_, 0),
scsi_device_(scsi_bus_.add_device()),
video_output_(ram_),
video_(ram_),
sound_generator_(audio_queue_),
speaker_(sound_generator_) {
memset(key_states_, 0, sizeof(key_states_));
@ -230,13 +232,15 @@ template <bool has_scsi_bus> class ConcreteMachine:
if(isReadOperation(operation)) {
*value = ram_[address];
} 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;
}
// 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
cycles += video_output_.get_cycles_until_next_ram_availability(int(cycles_since_display_update_.as_integral()) + 1);
// 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.
cycles += video_.last_valid()->get_cycles_until_next_ram_availability(video_.time_since_flush().template as<int>() + 1);
} else {
switch(address & 0xff0f) {
case 0xfe00:
@ -274,10 +278,8 @@ template <bool has_scsi_bus> class ConcreteMachine:
case 0xfe08: case 0xfe09: case 0xfe0a: case 0xfe0b:
case 0xfe0c: case 0xfe0d: case 0xfe0e: case 0xfe0f:
if(!isReadOperation(operation)) {
update_display();
video_output_.write(address, *value);
video_access_range_ = video_output_.get_memory_access_range();
queue_next_display_interrupt();
video_->write(address, *value);
video_access_range_ = video_.last_valid()->get_memory_access_range();
}
break;
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));
if(cycles_since_audio_update_ > Cycles(16384)) update_audio();
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(plus3_) plus3_->run_for(Cycles(4*int(cycles)));
if(shift_restart_counter_) {
@ -517,25 +515,25 @@ template <bool has_scsi_bus> class ConcreteMachine:
}
forceinline void flush() {
update_display();
video_.flush();
update_audio();
audio_queue_.perform();
}
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 {
return video_output_.get_scaled_scan_status();
return video_->get_scaled_scan_status();
}
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 {
return video_output_.get_display_type();
return video_->get_display_type();
}
Outputs::Speaker::Speaker *get_speaker() final {
@ -693,18 +691,6 @@ template <bool has_scsi_bus> class ConcreteMachine:
}
// 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() {
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_;
// Counters related to simultaneous subsystems
Cycles cycles_since_display_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};
// Tape
@ -794,7 +777,7 @@ template <bool has_scsi_bus> class ConcreteMachine:
}
// Outputs
VideoOutput video_output_;
JustInTimeActor<VideoOutput, Cycles> video_;
Concurrency::DeferringAsyncTaskQueue audio_queue_;
SoundGenerator sound_generator_;

View File

@ -234,7 +234,23 @@ void VideoOutput::output_pixels(int number_of_cycles) {
void VideoOutput::run_for(const Cycles cycles) {
int number_of_cycles = int(cycles.as_integral());
const auto start_position = output_position_;
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) {
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_);
@ -354,36 +370,30 @@ void VideoOutput::setup_base_address() {
// MARK: - Interrupts
VideoOutput::Interrupt VideoOutput::get_next_interrupt() {
VideoOutput::Interrupt interrupt;
Cycles VideoOutput::get_next_sequence_point() {
if(output_position_ < real_time_clock_interrupt_1) {
interrupt.cycles = real_time_clock_interrupt_1 - output_position_;
interrupt.interrupt = RealTimeClock;
return interrupt;
return real_time_clock_interrupt_1 - output_position_;
}
if(output_position_ < display_end_interrupt_1) {
interrupt.cycles = display_end_interrupt_1 - output_position_;
interrupt.interrupt = DisplayEnd;
return interrupt;
return display_end_interrupt_1 - output_position_;
}
if(output_position_ < real_time_clock_interrupt_2) {
interrupt.cycles = real_time_clock_interrupt_2 - output_position_;
interrupt.interrupt = RealTimeClock;
return interrupt;
return real_time_clock_interrupt_2 - output_position_;
}
if(output_position_ < display_end_interrupt_2) {
interrupt.cycles = display_end_interrupt_2 - output_position_;
interrupt.interrupt = DisplayEnd;
return interrupt;
return display_end_interrupt_2 - output_position_;
}
interrupt.cycles = real_time_clock_interrupt_1 + cycles_per_frame - output_position_;
interrupt.interrupt = RealTimeClock;
return interrupt;
return real_time_clock_interrupt_1 + cycles_per_frame - output_position_;
}
Electron::Interrupt VideoOutput::get_interrupts() {
const auto interrupts = interrupts_;
interrupts_ = Electron::Interrupt(0);
return interrupts;
}
// MARK: - RAM timing and access information

View File

@ -54,15 +54,6 @@ class VideoOutput {
*/
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.
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.
*/
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)
@ -136,6 +132,8 @@ class VideoOutput {
void emplace_pixel_line();
std::size_t screen_map_pointer_ = 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.
JustInTimeActor<Amstrad::FDC, 1, 1, Cycles> fdc_;
JustInTimeActor<Amstrad::FDC, Cycles> fdc_;
// MARK: - Automatic startup.
Cycles duration_to_press_enter_;