mirror of
https://github.com/TomHarte/CLK.git
synced 2025-02-18 01:30:56 +00:00
Move responsibility for clock division; reinstate vsync interrupt.
This commit is contained in:
parent
4a2dcff028
commit
ae6cf69449
@ -21,7 +21,7 @@ namespace InstructionSet::ARM {
|
|||||||
template <Model model, typename MemoryT>
|
template <Model model, typename MemoryT>
|
||||||
struct Executor {
|
struct Executor {
|
||||||
template <typename... Args>
|
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,
|
/// @returns @c true if @c condition implies an appropriate perform call should be made for this instruction,
|
||||||
/// @c false otherwise.
|
/// @c false otherwise.
|
||||||
|
@ -53,6 +53,7 @@ class ConcreteMachine:
|
|||||||
//
|
//
|
||||||
// The implementation of this is coupled to the ClockRate above, hence its
|
// The implementation of this is coupled to the ClockRate above, hence its
|
||||||
// appearance here.
|
// appearance here.
|
||||||
|
template <int video_divider>
|
||||||
void macro_tick() {
|
void macro_tick() {
|
||||||
macro_counter_ -= 24;
|
macro_counter_ -= 24;
|
||||||
|
|
||||||
@ -60,34 +61,41 @@ class ConcreteMachine:
|
|||||||
// Hence, required ticks are:
|
// Hence, required ticks are:
|
||||||
//
|
//
|
||||||
// * CPU: 24;
|
// * CPU: 24;
|
||||||
// * video: 12;
|
// * video: 24 / video_divider;
|
||||||
// * timers: 2;
|
// * timers: 2;
|
||||||
// * sound: 1.
|
// * sound: 1.
|
||||||
|
|
||||||
tick_cpu(); tick_cpu(); tick_video();
|
tick_cpu_video<0, video_divider>(); tick_cpu_video<1, video_divider>();
|
||||||
tick_cpu(); tick_cpu(); tick_video();
|
tick_cpu_video<2, video_divider>(); tick_cpu_video<3, video_divider>();
|
||||||
tick_cpu(); tick_cpu(); tick_video();
|
tick_cpu_video<4, video_divider>(); tick_cpu_video<5, video_divider>();
|
||||||
tick_cpu(); tick_cpu(); tick_video();
|
tick_cpu_video<6, video_divider>(); tick_cpu_video<7, video_divider>();
|
||||||
tick_cpu(); tick_cpu(); tick_video();
|
tick_cpu_video<8, video_divider>(); tick_cpu_video<9, video_divider>();
|
||||||
tick_cpu(); tick_cpu(); tick_video();
|
tick_cpu_video<10, video_divider>(); tick_cpu_video<11, video_divider>();
|
||||||
tick_timers();
|
tick_timers();
|
||||||
|
|
||||||
tick_cpu(); tick_cpu(); tick_video();
|
tick_cpu_video<12, video_divider>(); tick_cpu_video<13, video_divider>();
|
||||||
tick_cpu(); tick_cpu(); tick_video();
|
tick_cpu_video<14, video_divider>(); tick_cpu_video<15, video_divider>();
|
||||||
tick_cpu(); tick_cpu(); tick_video();
|
tick_cpu_video<16, video_divider>(); tick_cpu_video<17, video_divider>();
|
||||||
tick_cpu(); tick_cpu(); tick_video();
|
tick_cpu_video<18, video_divider>(); tick_cpu_video<19, video_divider>();
|
||||||
tick_cpu(); tick_cpu(); tick_video();
|
tick_cpu_video<20, video_divider>(); tick_cpu_video<21, video_divider>();
|
||||||
tick_cpu(); tick_cpu(); tick_video();
|
|
||||||
tick_timers();
|
tick_timers();
|
||||||
tick_sound();
|
tick_sound();
|
||||||
}
|
}
|
||||||
int macro_counter_ = 0;
|
int macro_counter_ = 0;
|
||||||
|
|
||||||
|
template <int offset, int video_divider>
|
||||||
|
void tick_cpu_video() {
|
||||||
|
tick_cpu();
|
||||||
|
if constexpr (!(offset % video_divider)) {
|
||||||
|
tick_video();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ConcreteMachine(
|
ConcreteMachine(
|
||||||
const Analyser::Static::Target &target,
|
const Analyser::Static::Target &target,
|
||||||
const ROMMachine::ROMFetcher &rom_fetcher
|
const ROMMachine::ROMFetcher &rom_fetcher
|
||||||
) : executor_(*this) {
|
) : executor_(*this, *this) {
|
||||||
set_clock_rate(ClockRate);
|
set_clock_rate(ClockRate);
|
||||||
|
|
||||||
constexpr ROM::Name risc_os = ROM::Name::AcornRISCOS319;
|
constexpr ROM::Name risc_os = ROM::Name::AcornRISCOS319;
|
||||||
@ -113,6 +121,10 @@ class ConcreteMachine:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void update_clock_rates() {
|
||||||
|
printf("");
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// MARK: - ScanProducer.
|
// MARK: - ScanProducer.
|
||||||
void set_scan_target(Outputs::Display::ScanTarget *scan_target) override {
|
void set_scan_target(Outputs::Display::ScanTarget *scan_target) override {
|
||||||
@ -131,9 +143,16 @@ class ConcreteMachine:
|
|||||||
macro_counter_ += cycles.as<int>();
|
macro_counter_ += cycles.as<int>();
|
||||||
|
|
||||||
while(macro_counter_ > 0) {
|
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() {
|
void tick_cpu() {
|
||||||
static uint32_t last_pc = 0;
|
static uint32_t last_pc = 0;
|
||||||
@ -225,7 +244,7 @@ class ConcreteMachine:
|
|||||||
|
|
||||||
// MARK: - ARM execution
|
// MARK: - ARM execution
|
||||||
static constexpr auto arm_model = InstructionSet::ARM::Model::ARMv2;
|
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_;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -58,7 +58,7 @@ namespace InterruptRequests {
|
|||||||
static constexpr int FIQ = 0x02;
|
static constexpr int FIQ = 0x02;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename InterruptObserverT>
|
template <typename InterruptObserverT, typename ClockRateObserverT>
|
||||||
struct InputOutputController {
|
struct InputOutputController {
|
||||||
int interrupt_mask() const {
|
int interrupt_mask() const {
|
||||||
return
|
return
|
||||||
@ -314,11 +314,11 @@ struct InputOutputController {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
InputOutputController(InterruptObserverT &observer, const uint8_t *ram) :
|
InputOutputController(InterruptObserverT &observer, ClockRateObserverT &clock_observer, const uint8_t *ram) :
|
||||||
observer_(observer),
|
observer_(observer),
|
||||||
keyboard_(serial_),
|
keyboard_(serial_),
|
||||||
sound_(*this, ram),
|
sound_(*this, ram),
|
||||||
video_(*this, sound_, ram)
|
video_(*this, clock_observer, sound_, ram)
|
||||||
{
|
{
|
||||||
irq_a_.status = IRQA::SetAlways | IRQA::PowerOnReset;
|
irq_a_.status = IRQA::SetAlways | IRQA::PowerOnReset;
|
||||||
irq_b_.status = 0x00;
|
irq_b_.status = 0x00;
|
||||||
@ -388,7 +388,7 @@ private:
|
|||||||
|
|
||||||
// Audio and video.
|
// Audio and video.
|
||||||
Sound<InputOutputController> sound_;
|
Sound<InputOutputController> sound_;
|
||||||
Video<InputOutputController, Sound<InputOutputController>> video_;
|
Video<InputOutputController, ClockRateObserverT, Sound<InputOutputController>> video_;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -28,11 +28,12 @@ static_assert(BitMask<15, 15>::value == 32768);
|
|||||||
static_assert(BitMask<15, 0>::value == 0xffff);
|
static_assert(BitMask<15, 0>::value == 0xffff);
|
||||||
static_assert(BitMask<15, 14>::value == 49152);
|
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.
|
/// 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 {
|
struct MemoryController {
|
||||||
MemoryController(InterruptObserverT &observer) :
|
MemoryController(InterruptObserverT &observer, ClockRateObserverT &clock_rate_observer) :
|
||||||
ioc_(observer, ram_.data()) {}
|
ioc_(observer, clock_rate_observer, ram_.data()) {}
|
||||||
|
|
||||||
int interrupt_mask() const {
|
int interrupt_mask() const {
|
||||||
return ioc_.interrupt_mask();
|
return ioc_.interrupt_mask();
|
||||||
@ -240,7 +241,7 @@ struct MemoryController {
|
|||||||
bool has_moved_rom_ = false;
|
bool has_moved_rom_ = false;
|
||||||
std::array<uint8_t, 4*1024*1024> ram_{};
|
std::array<uint8_t, 4*1024*1024> ram_{};
|
||||||
std::array<uint8_t, 2*1024*1024> rom_;
|
std::array<uint8_t, 2*1024*1024> rom_;
|
||||||
InputOutputController<InterruptObserverT> ioc_;
|
InputOutputController<InterruptObserverT, ClockRateObserverT> ioc_;
|
||||||
|
|
||||||
template <typename IntT>
|
template <typename IntT>
|
||||||
IntT &physical_ram(uint32_t address) {
|
IntT &physical_ram(uint32_t address) {
|
||||||
|
@ -16,10 +16,11 @@
|
|||||||
|
|
||||||
namespace Archimedes {
|
namespace Archimedes {
|
||||||
|
|
||||||
template <typename InterruptObserverT, typename SoundT>
|
template <typename InterruptObserverT, typename ClockRateObserverT, typename SoundT>
|
||||||
struct Video {
|
struct Video {
|
||||||
Video(InterruptObserverT &observer, SoundT &sound, const uint8_t *ram) :
|
Video(InterruptObserverT &interrupt_observer, ClockRateObserverT &clock_rate_observer, SoundT &sound, const uint8_t *ram) :
|
||||||
observer_(observer),
|
interrupt_observer_(interrupt_observer),
|
||||||
|
clock_rate_observer_(clock_rate_observer),
|
||||||
sound_(sound),
|
sound_(sound),
|
||||||
ram_(ram),
|
ram_(ram),
|
||||||
crt_(Outputs::Display::InputDataType::Red4Green4Blue4) {
|
crt_(Outputs::Display::InputDataType::Red4Green4Blue4) {
|
||||||
@ -109,26 +110,27 @@ struct Video {
|
|||||||
|
|
||||||
void tick() {
|
void tick() {
|
||||||
// Pick new horizontal state, possibly rolling over into the vertical.
|
// Pick new horizontal state, possibly rolling over into the vertical.
|
||||||
horizontal_state_.increment_position(clock_divider_);
|
horizontal_state_.increment_position();
|
||||||
horizontal_state_.phase =
|
horizontal_state_.phase =
|
||||||
horizontal_timing_.phase_after(
|
horizontal_timing_.phase_after(
|
||||||
horizontal_state_.position,
|
horizontal_state_.position,
|
||||||
horizontal_state_.phase,
|
horizontal_state_.phase);
|
||||||
clock_divider_);
|
|
||||||
|
|
||||||
if(horizontal_state_.position == horizontal_timing_.period * clock_divider_) {
|
if(horizontal_state_.position == horizontal_timing_.period ) {
|
||||||
horizontal_state_.position = 0;
|
horizontal_state_.position = 0;
|
||||||
|
|
||||||
vertical_state_.increment_position(1);
|
vertical_state_.increment_position();
|
||||||
vertical_state_.phase =
|
vertical_state_.phase =
|
||||||
vertical_timing_.phase_after(
|
vertical_timing_.phase_after(
|
||||||
vertical_state_.position,
|
vertical_state_.position,
|
||||||
vertical_state_.phase,
|
vertical_state_.phase);
|
||||||
1);
|
|
||||||
|
|
||||||
if(vertical_state_.position == vertical_timing_.period) {
|
if(vertical_state_.position == vertical_timing_.period) {
|
||||||
vertical_state_.position = 0;
|
vertical_state_.position = 0;
|
||||||
address_ = frame_start_;
|
address_ = frame_start_;
|
||||||
|
|
||||||
|
entered_sync_ = true;
|
||||||
|
interrupt_observer_.update_interrupts();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -148,7 +150,7 @@ struct Video {
|
|||||||
|
|
||||||
// Possibly output something. TODO: with actual pixels.
|
// Possibly output something. TODO: with actual pixels.
|
||||||
if(new_phase != phase_) {
|
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_) {
|
switch(phase_) {
|
||||||
case Phase::Sync: crt_.output_sync(duration); break;
|
case Phase::Sync: crt_.output_sync(duration); break;
|
||||||
@ -182,9 +184,14 @@ struct Video {
|
|||||||
Outputs::CRT::CRT &crt() { return crt_; }
|
Outputs::CRT::CRT &crt() { return crt_; }
|
||||||
const Outputs::CRT::CRT &crt() const { return crt_; }
|
const Outputs::CRT::CRT &crt() const { return crt_; }
|
||||||
|
|
||||||
|
int clock_divider() const {
|
||||||
|
return clock_divider_;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Log::Logger<Log::Source::ARMIOC> logger;
|
Log::Logger<Log::Source::ARMIOC> logger;
|
||||||
InterruptObserverT &observer_;
|
InterruptObserverT &interrupt_observer_;
|
||||||
|
ClockRateObserverT &clock_rate_observer_;
|
||||||
SoundT &sound_;
|
SoundT &sound_;
|
||||||
|
|
||||||
// In the current version of this code, video DMA occurrs costlessly,
|
// In the current version of this code, video DMA occurrs costlessly,
|
||||||
@ -200,9 +207,9 @@ private:
|
|||||||
uint32_t position = 0;
|
uint32_t position = 0;
|
||||||
Phase phase = Phase::Sync;
|
Phase phase = Phase::Sync;
|
||||||
|
|
||||||
void increment_position(uint32_t divider) {
|
void increment_position() {
|
||||||
++position;
|
++position;
|
||||||
if(position == 1024 * divider) position = 0;
|
if(position == 1024) position = 0;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
State horizontal_state_, vertical_state_;
|
State horizontal_state_, vertical_state_;
|
||||||
@ -229,13 +236,13 @@ private:
|
|||||||
uint32_t cursor_start = 0;
|
uint32_t cursor_start = 0;
|
||||||
uint32_t cursor_end = 0;
|
uint32_t cursor_end = 0;
|
||||||
|
|
||||||
Phase phase_after(uint32_t position, Phase current_phase, uint32_t divider) {
|
Phase phase_after(uint32_t position, Phase current_phase) {
|
||||||
if(position == sync_width * divider) return Phase::Blank;
|
if(position == sync_width) return Phase::Blank;
|
||||||
if(position == border_start * divider) return Phase::Border;
|
if(position == border_start) return Phase::Border;
|
||||||
if(position == display_start * divider) return Phase::Display;
|
if(position == display_start) return Phase::Display;
|
||||||
if(position == display_end * divider) return Phase::Border;
|
if(position == display_end) return Phase::Border;
|
||||||
if(position == border_end * divider) return Phase::Blank;
|
if(position == border_end) return Phase::Blank;
|
||||||
if(position == period * divider) return Phase::Sync;
|
if(position == period) return Phase::Sync;
|
||||||
return current_phase;
|
return current_phase;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -270,6 +277,7 @@ private:
|
|||||||
Outputs::CRT::PAL::ColourCycleDenominator,
|
Outputs::CRT::PAL::ColourCycleDenominator,
|
||||||
Outputs::CRT::PAL::VerticalSyncLength,
|
Outputs::CRT::PAL::VerticalSyncLength,
|
||||||
Outputs::CRT::PAL::AlternatesPhase);
|
Outputs::CRT::PAL::AlternatesPhase);
|
||||||
|
clock_rate_observer_.update_clock_rates();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user