mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-22 12:33:29 +00:00
Takes a run at interrupts.
This commit is contained in:
parent
135134acfd
commit
ab2ad70885
@ -9,6 +9,9 @@
|
|||||||
#ifndef Video_hpp
|
#ifndef Video_hpp
|
||||||
#define Video_hpp
|
#define Video_hpp
|
||||||
|
|
||||||
|
|
||||||
|
#include "../../../ClockReceiver/ClockReceiver.hpp"
|
||||||
|
|
||||||
namespace Sinclair {
|
namespace Sinclair {
|
||||||
namespace ZXSpectrum {
|
namespace ZXSpectrum {
|
||||||
|
|
||||||
@ -16,8 +19,103 @@ enum class VideoTiming {
|
|||||||
Plus3
|
Plus3
|
||||||
};
|
};
|
||||||
|
|
||||||
template <VideoTiming timing> class Video {
|
/*
|
||||||
|
Timing notes:
|
||||||
|
|
||||||
|
As of the +2a/+3:
|
||||||
|
|
||||||
|
311 lines, 228 cycles/line
|
||||||
|
Delays begin at 14361, follow the pattern 1, 0, 7, 6, 5, 4, 3, 2; run for 129 cycles/line.
|
||||||
|
Possibly delays only affect actual reads and writes; documentation is unclear.
|
||||||
|
|
||||||
|
Unknowns, to me, presently:
|
||||||
|
|
||||||
|
How long the interrupt line held for.
|
||||||
|
|
||||||
|
So...
|
||||||
|
|
||||||
|
Probably two bytes of video and attribute are fetched in each 8-cycle block,
|
||||||
|
with 16 such blocks therefore providing the whole visible display, an island
|
||||||
|
within 28.5 blocks horizontally.
|
||||||
|
|
||||||
|
14364 is 228*63, so I I guess almost 63 lines run from the start of vertical
|
||||||
|
blank through to the top of the display, implying 56 lines on to vertical blank.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
template <VideoTiming timing> class Video {
|
||||||
|
private:
|
||||||
|
struct Timings {
|
||||||
|
int cycles_per_line;
|
||||||
|
int lines_per_frame;
|
||||||
|
int first_delay;
|
||||||
|
int first_border;
|
||||||
|
int delays[16];
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr Timings get_timings() {
|
||||||
|
constexpr Timings result = {
|
||||||
|
.cycles_per_line = 228 * 2,
|
||||||
|
.lines_per_frame = 311,
|
||||||
|
.first_delay = 14361 * 2,
|
||||||
|
.first_border = 14490 * 2,
|
||||||
|
.delays = {
|
||||||
|
2, 1,
|
||||||
|
0, 0,
|
||||||
|
14, 13,
|
||||||
|
12, 11,
|
||||||
|
10, 9,
|
||||||
|
8, 7,
|
||||||
|
6, 5,
|
||||||
|
4, 3,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
void run_for(HalfCycles duration) {
|
||||||
|
constexpr auto timings = get_timings();
|
||||||
|
|
||||||
|
// Advance time. TODO: all the drawing.
|
||||||
|
time_since_interrupt_ = (time_since_interrupt_ + duration.as<int>()) % (timings.cycles_per_line * timings.lines_per_frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// TODO: how long is the interrupt line held for?
|
||||||
|
static constexpr int interrupt_duration = 48;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
HalfCycles get_next_sequence_point() {
|
||||||
|
if(time_since_interrupt_ < interrupt_duration) {
|
||||||
|
return HalfCycles(interrupt_duration - time_since_interrupt_);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr auto timings = get_timings();
|
||||||
|
return timings.cycles_per_line * timings.lines_per_frame - time_since_interrupt_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool get_interrupt_line() const {
|
||||||
|
return time_since_interrupt_ < interrupt_duration;
|
||||||
|
}
|
||||||
|
|
||||||
|
int access_delay() const {
|
||||||
|
constexpr auto timings = get_timings();
|
||||||
|
if(time_since_interrupt_ < timings.first_delay) return 0;
|
||||||
|
|
||||||
|
const int time_since = time_since_interrupt_ - timings.first_delay;
|
||||||
|
const int lines = time_since / timings.cycles_per_line;
|
||||||
|
if(lines >= 192) return 0;
|
||||||
|
|
||||||
|
const int line_position = time_since % timings.cycles_per_line;
|
||||||
|
if(line_position >= timings.first_border - timings.first_delay) return 0;
|
||||||
|
|
||||||
|
return timings.delays[line_position & 7];
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
int time_since_interrupt_ = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -82,6 +82,7 @@ template<Model model> class ConcreteMachine:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void flush() {
|
void flush() {
|
||||||
|
video_.flush();
|
||||||
update_audio();
|
update_audio();
|
||||||
audio_queue_.perform();
|
audio_queue_.perform();
|
||||||
}
|
}
|
||||||
@ -102,6 +103,11 @@ template<Model model> class ConcreteMachine:
|
|||||||
forceinline HalfCycles perform_machine_cycle(const CPU::Z80::PartialMachineCycle &cycle) {
|
forceinline HalfCycles perform_machine_cycle(const CPU::Z80::PartialMachineCycle &cycle) {
|
||||||
time_since_audio_update_ += cycle.length;
|
time_since_audio_update_ += cycle.length;
|
||||||
|
|
||||||
|
video_ += cycle.length;
|
||||||
|
if(video_.did_flush()) {
|
||||||
|
z80_.set_interrupt_line(video_.last_valid()->get_interrupt_line());
|
||||||
|
}
|
||||||
|
|
||||||
// Ignore all but terminal cycles.
|
// Ignore all but terminal cycles.
|
||||||
// TODO: I doubt this is correct for timing.
|
// TODO: I doubt this is correct for timing.
|
||||||
if(!cycle.is_terminal()) return HalfCycles(0);
|
if(!cycle.is_terminal()) return HalfCycles(0);
|
||||||
@ -287,7 +293,7 @@ template<Model model> class ConcreteMachine:
|
|||||||
|
|
||||||
// MARK: - Video.
|
// MARK: - Video.
|
||||||
static constexpr VideoTiming video_timing = VideoTiming::Plus3;
|
static constexpr VideoTiming video_timing = VideoTiming::Plus3;
|
||||||
Video<video_timing> video_;
|
JustInTimeActor<Video<video_timing>> video_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user