1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-11-28 21:49:27 +00:00

Move responsibility for clock division; reinstate vsync interrupt.

This commit is contained in:
Thomas Harte 2024-03-22 10:01:34 -04:00
parent 4a2dcff028
commit ae6cf69449
5 changed files with 74 additions and 46 deletions

View File

@ -21,7 +21,7 @@ namespace InstructionSet::ARM {
template <Model model, typename MemoryT>
struct Executor {
template <typename... Args>
Executor(Args &&...args) : bus(std::forward<Args...>(args)...) {}
Executor(Args &&...args) : bus(std::forward<Args>(args)...) {}
/// @returns @c true if @c condition implies an appropriate perform call should be made for this instruction,
/// @c false otherwise.

View File

@ -53,6 +53,7 @@ class ConcreteMachine:
//
// The implementation of this is coupled to the ClockRate above, hence its
// appearance here.
template <int video_divider>
void macro_tick() {
macro_counter_ -= 24;
@ -60,34 +61,41 @@ class ConcreteMachine:
// Hence, required ticks are:
//
// * CPU: 24;
// * video: 12;
// * video: 24 / video_divider;
// * timers: 2;
// * sound: 1.
tick_cpu(); tick_cpu(); tick_video();
tick_cpu(); tick_cpu(); tick_video();
tick_cpu(); tick_cpu(); tick_video();
tick_cpu(); tick_cpu(); tick_video();
tick_cpu(); tick_cpu(); tick_video();
tick_cpu(); tick_cpu(); tick_video();
tick_cpu_video<0, video_divider>(); tick_cpu_video<1, video_divider>();
tick_cpu_video<2, video_divider>(); tick_cpu_video<3, video_divider>();
tick_cpu_video<4, video_divider>(); tick_cpu_video<5, video_divider>();
tick_cpu_video<6, video_divider>(); tick_cpu_video<7, video_divider>();
tick_cpu_video<8, video_divider>(); tick_cpu_video<9, video_divider>();
tick_cpu_video<10, video_divider>(); tick_cpu_video<11, video_divider>();
tick_timers();
tick_cpu(); tick_cpu(); tick_video();
tick_cpu(); tick_cpu(); tick_video();
tick_cpu(); tick_cpu(); tick_video();
tick_cpu(); tick_cpu(); tick_video();
tick_cpu(); tick_cpu(); tick_video();
tick_cpu(); tick_cpu(); tick_video();
tick_cpu_video<12, video_divider>(); tick_cpu_video<13, video_divider>();
tick_cpu_video<14, video_divider>(); tick_cpu_video<15, video_divider>();
tick_cpu_video<16, video_divider>(); tick_cpu_video<17, video_divider>();
tick_cpu_video<18, video_divider>(); tick_cpu_video<19, video_divider>();
tick_cpu_video<20, video_divider>(); tick_cpu_video<21, video_divider>();
tick_timers();
tick_sound();
}
int macro_counter_ = 0;
template <int offset, int video_divider>
void tick_cpu_video() {
tick_cpu();
if constexpr (!(offset % video_divider)) {
tick_video();
}
}
public:
ConcreteMachine(
const Analyser::Static::Target &target,
const ROMMachine::ROMFetcher &rom_fetcher
) : executor_(*this) {
) : executor_(*this, *this) {
set_clock_rate(ClockRate);
constexpr ROM::Name risc_os = ROM::Name::AcornRISCOS319;
@ -113,6 +121,10 @@ class ConcreteMachine:
}
}
void update_clock_rates() {
printf("");
}
private:
// MARK: - ScanProducer.
void set_scan_target(Outputs::Display::ScanTarget *scan_target) override {
@ -131,9 +143,16 @@ class ConcreteMachine:
macro_counter_ += cycles.as<int>();
while(macro_counter_ > 0) {
macro_tick();
switch(video_divider_) {
default: macro_tick<2>(); break;
case 3: macro_tick<3>(); break;
case 4: macro_tick<4>(); break;
case 6: macro_tick<6>(); break;
}
}
}
int video_divider_ = 1;
void tick_cpu() {
static uint32_t last_pc = 0;
@ -225,7 +244,7 @@ class ConcreteMachine:
// MARK: - ARM execution
static constexpr auto arm_model = InstructionSet::ARM::Model::ARMv2;
InstructionSet::ARM::Executor<arm_model, MemoryController<ConcreteMachine>> executor_;
InstructionSet::ARM::Executor<arm_model, MemoryController<ConcreteMachine, ConcreteMachine>> executor_;
};
}

View File

@ -58,7 +58,7 @@ namespace InterruptRequests {
static constexpr int FIQ = 0x02;
};
template <typename InterruptObserverT>
template <typename InterruptObserverT, typename ClockRateObserverT>
struct InputOutputController {
int interrupt_mask() const {
return
@ -314,11 +314,11 @@ struct InputOutputController {
return true;
}
InputOutputController(InterruptObserverT &observer, const uint8_t *ram) :
InputOutputController(InterruptObserverT &observer, ClockRateObserverT &clock_observer, const uint8_t *ram) :
observer_(observer),
keyboard_(serial_),
sound_(*this, ram),
video_(*this, sound_, ram)
video_(*this, clock_observer, sound_, ram)
{
irq_a_.status = IRQA::SetAlways | IRQA::PowerOnReset;
irq_b_.status = 0x00;
@ -388,7 +388,7 @@ private:
// Audio and video.
Sound<InputOutputController> sound_;
Video<InputOutputController, Sound<InputOutputController>> video_;
Video<InputOutputController, ClockRateObserverT, Sound<InputOutputController>> video_;
};
}

View File

@ -28,11 +28,12 @@ static_assert(BitMask<15, 15>::value == 32768);
static_assert(BitMask<15, 0>::value == 0xffff);
static_assert(BitMask<15, 14>::value == 49152);
/// Models the MEMC, making this the Archimedes bus. Owns various other chips on the bus as a result.
template <typename InterruptObserverT>
template <typename InterruptObserverT, typename ClockRateObserverT>
struct MemoryController {
MemoryController(InterruptObserverT &observer) :
ioc_(observer, ram_.data()) {}
MemoryController(InterruptObserverT &observer, ClockRateObserverT &clock_rate_observer) :
ioc_(observer, clock_rate_observer, ram_.data()) {}
int interrupt_mask() const {
return ioc_.interrupt_mask();
@ -240,7 +241,7 @@ struct MemoryController {
bool has_moved_rom_ = false;
std::array<uint8_t, 4*1024*1024> ram_{};
std::array<uint8_t, 2*1024*1024> rom_;
InputOutputController<InterruptObserverT> ioc_;
InputOutputController<InterruptObserverT, ClockRateObserverT> ioc_;
template <typename IntT>
IntT &physical_ram(uint32_t address) {

View File

@ -16,10 +16,11 @@
namespace Archimedes {
template <typename InterruptObserverT, typename SoundT>
template <typename InterruptObserverT, typename ClockRateObserverT, typename SoundT>
struct Video {
Video(InterruptObserverT &observer, SoundT &sound, const uint8_t *ram) :
observer_(observer),
Video(InterruptObserverT &interrupt_observer, ClockRateObserverT &clock_rate_observer, SoundT &sound, const uint8_t *ram) :
interrupt_observer_(interrupt_observer),
clock_rate_observer_(clock_rate_observer),
sound_(sound),
ram_(ram),
crt_(Outputs::Display::InputDataType::Red4Green4Blue4) {
@ -109,26 +110,27 @@ struct Video {
void tick() {
// Pick new horizontal state, possibly rolling over into the vertical.
horizontal_state_.increment_position(clock_divider_);
horizontal_state_.increment_position();
horizontal_state_.phase =
horizontal_timing_.phase_after(
horizontal_state_.position,
horizontal_state_.phase,
clock_divider_);
horizontal_state_.phase);
if(horizontal_state_.position == horizontal_timing_.period * clock_divider_) {
if(horizontal_state_.position == horizontal_timing_.period ) {
horizontal_state_.position = 0;
vertical_state_.increment_position(1);
vertical_state_.increment_position();
vertical_state_.phase =
vertical_timing_.phase_after(
vertical_state_.position,
vertical_state_.phase,
1);
vertical_state_.phase);
if(vertical_state_.position == vertical_timing_.period) {
vertical_state_.position = 0;
address_ = frame_start_;
entered_sync_ = true;
interrupt_observer_.update_interrupts();
}
}
@ -148,7 +150,7 @@ struct Video {
// Possibly output something. TODO: with actual pixels.
if(new_phase != phase_) {
const auto duration = static_cast<int>(time_in_phase_ / clock_divider_);
const auto duration = static_cast<int>(time_in_phase_);
switch(phase_) {
case Phase::Sync: crt_.output_sync(duration); break;
@ -182,9 +184,14 @@ struct Video {
Outputs::CRT::CRT &crt() { return crt_; }
const Outputs::CRT::CRT &crt() const { return crt_; }
int clock_divider() const {
return clock_divider_;
}
private:
Log::Logger<Log::Source::ARMIOC> logger;
InterruptObserverT &observer_;
InterruptObserverT &interrupt_observer_;
ClockRateObserverT &clock_rate_observer_;
SoundT &sound_;
// In the current version of this code, video DMA occurrs costlessly,
@ -200,9 +207,9 @@ private:
uint32_t position = 0;
Phase phase = Phase::Sync;
void increment_position(uint32_t divider) {
void increment_position() {
++position;
if(position == 1024 * divider) position = 0;
if(position == 1024) position = 0;
}
};
State horizontal_state_, vertical_state_;
@ -229,13 +236,13 @@ private:
uint32_t cursor_start = 0;
uint32_t cursor_end = 0;
Phase phase_after(uint32_t position, Phase current_phase, uint32_t divider) {
if(position == sync_width * divider) return Phase::Blank;
if(position == border_start * divider) return Phase::Border;
if(position == display_start * divider) return Phase::Display;
if(position == display_end * divider) return Phase::Border;
if(position == border_end * divider) return Phase::Blank;
if(position == period * divider) return Phase::Sync;
Phase phase_after(uint32_t position, Phase current_phase) {
if(position == sync_width) return Phase::Blank;
if(position == border_start) return Phase::Border;
if(position == display_start) return Phase::Display;
if(position == display_end) return Phase::Border;
if(position == border_end) return Phase::Blank;
if(position == period) return Phase::Sync;
return current_phase;
}
};
@ -270,6 +277,7 @@ private:
Outputs::CRT::PAL::ColourCycleDenominator,
Outputs::CRT::PAL::VerticalSyncLength,
Outputs::CRT::PAL::AlternatesPhase);
clock_rate_observer_.update_clock_rates();
}
};