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:
parent
4a2dcff028
commit
ae6cf69449
@ -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.
|
||||
|
@ -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_;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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_;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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();
|
||||
}
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user