mirror of
https://github.com/TomHarte/CLK.git
synced 2024-12-24 12:30:17 +00:00
Merge pull request #926 from TomHarte/SimplifiedTiming
Attempts more cleanly to express ZX Spectrum timing.
This commit is contained in:
commit
5ea605ccf7
@ -63,106 +63,34 @@ template <VideoTiming timing> class Video {
|
||||
// Number of cycles after first pixel fetch at which interrupt is first signalled.
|
||||
int interrupt_time;
|
||||
|
||||
// Contention to apply, in half-cycles, as a function of number of half cycles since
|
||||
// Contention to apply, in whole cycles, as a function of number of whole cycles since
|
||||
// contention began.
|
||||
int delays[16];
|
||||
int delays[8];
|
||||
|
||||
constexpr Timings(int cycles_per_line, int lines_per_frame, int contention_leadin, int contention_duration, int interrupt_offset, const int *delays) noexcept :
|
||||
cycles_per_line(cycles_per_line * 2),
|
||||
lines_per_frame(lines_per_frame),
|
||||
contention_leadin(contention_leadin * 2),
|
||||
contention_duration(contention_duration * 2),
|
||||
interrupt_time((cycles_per_line * lines_per_frame - interrupt_offset - contention_leadin) * 2),
|
||||
delays{ delays[0] * 2, delays[1] * 2, delays[2] * 2, delays[3] * 2, delays[4] * 2, delays[5] * 2, delays[6] * 2, delays[7] * 2}
|
||||
{}
|
||||
};
|
||||
|
||||
static constexpr Timings get_timings() {
|
||||
if constexpr (timing == VideoTiming::Plus3) {
|
||||
// Amstrad gate array timings, classic statement:
|
||||
//
|
||||
// Contention begins 14361 cycles "after interrupt" and follows the pattern [1, 0, 7, 6 5 4, 3, 2].
|
||||
// The first four bytes of video are fetched at 14365–14368 cycles, in the order [pixels, attribute, pixels, attribute].
|
||||
//
|
||||
// For my purposes:
|
||||
//
|
||||
// Video fetching always begins at 0. Since there are 311*228 = 70908 cycles per frame, and the interrupt
|
||||
// should "occur" (I assume: begin) 14365 before that, it should actually begin at 70908 - 14365 = 56543.
|
||||
//
|
||||
// Contention begins four cycles before the first video fetch, so it begins at 70904. I don't currently
|
||||
// know whether the four cycles is true across all models, so it's given here as convention_leadin.
|
||||
//
|
||||
// ... except that empirically that all seems to be two cycles off. So maybe I misunderstand what the
|
||||
// contention patterns are supposed to indicate relative to MREQ? It's frustrating that all documentation
|
||||
// I can find is vaguely in terms of contention patterns, and what they mean isn't well-defined in terms
|
||||
// of regular Z80 signalling.
|
||||
constexpr Timings result = {
|
||||
.cycles_per_line = 228 * 2,
|
||||
.lines_per_frame = 311,
|
||||
|
||||
// i.e. video fetching begins five cycles after the start of the
|
||||
// contended memory pattern below; that should put a clear two
|
||||
// cycles between a Z80 access and the first video fetch.
|
||||
.contention_leadin = 5 * 2,
|
||||
.contention_duration = 129 * 2,
|
||||
|
||||
// i.e. interrupt is first signalled 14368 cycles before the first video fetch.
|
||||
.interrupt_time = (1 + 228*311 - 14365 - 5) * 2,
|
||||
|
||||
.delays = { // Should start at 14365
|
||||
2, 1,
|
||||
0, 0,
|
||||
14, 13,
|
||||
12, 11,
|
||||
10, 9,
|
||||
8, 7,
|
||||
6, 5,
|
||||
4, 3,
|
||||
}
|
||||
};
|
||||
|
||||
return result;
|
||||
constexpr int delays[] = {1, 0, 7, 6, 5, 4, 3, 2};
|
||||
return Timings(228, 311, 6, 129, 14365, delays);
|
||||
}
|
||||
|
||||
if constexpr (timing == VideoTiming::OneTwoEightK) {
|
||||
constexpr Timings result = {
|
||||
.cycles_per_line = 228 * 2,
|
||||
.lines_per_frame = 311,
|
||||
|
||||
.contention_leadin = 4 * 2,
|
||||
.contention_duration = 128 * 2,
|
||||
|
||||
.interrupt_time = (1 + 228*311 - 14361 - 4) * 2,
|
||||
|
||||
.delays = { // Should start at 14361.
|
||||
12, 11,
|
||||
10, 9,
|
||||
8, 7,
|
||||
6, 5,
|
||||
4, 3,
|
||||
2, 1,
|
||||
0, 0,
|
||||
0, 0,
|
||||
}
|
||||
};
|
||||
|
||||
return result;
|
||||
constexpr int delays[] = {6, 5, 4, 3, 2, 1, 0, 0};
|
||||
return Timings(228, 311, 4, 128, 14361, delays);
|
||||
}
|
||||
|
||||
if constexpr (timing == VideoTiming::FortyEightK) {
|
||||
constexpr Timings result = {
|
||||
.cycles_per_line = 224 * 2,
|
||||
.lines_per_frame = 312,
|
||||
|
||||
.contention_leadin = 4 * 2,
|
||||
.contention_duration = 128 * 2,
|
||||
|
||||
.interrupt_time = (1 + 224*312 - 14335 - 4) * 2,
|
||||
|
||||
.delays = { // Should start at 14335.
|
||||
12, 11,
|
||||
10, 9,
|
||||
8, 7,
|
||||
6, 5,
|
||||
4, 3,
|
||||
2, 1,
|
||||
0, 0,
|
||||
0, 0,
|
||||
}
|
||||
};
|
||||
|
||||
return result;
|
||||
constexpr int delays[] = {6, 5, 4, 3, 2, 1, 0, 0};
|
||||
return Timings(224, 312, 4, 128, 14335, delays);
|
||||
}
|
||||
}
|
||||
|
||||
@ -377,9 +305,10 @@ template <VideoTiming timing> class Video {
|
||||
@returns How many cycles the [ULA/gate array] would delay the CPU for if it were to recognise that contention
|
||||
needs to be applied in @c offset half-cycles from now.
|
||||
*/
|
||||
int access_delay(HalfCycles offset) const {
|
||||
HalfCycles access_delay(HalfCycles offset) const {
|
||||
constexpr auto timings = get_timings();
|
||||
const int delay_time = (time_into_frame_ + offset.as<int>() + timings.contention_leadin) % (timings.cycles_per_line * timings.lines_per_frame);
|
||||
assert(!(delay_time&1));
|
||||
|
||||
// Check for a time within the no-contention window.
|
||||
if(delay_time >= (191*timings.cycles_per_line + timings.contention_duration)) {
|
||||
@ -391,7 +320,7 @@ template <VideoTiming timing> class Video {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return timings.delays[time_into_line & 15];
|
||||
return HalfCycles(timings.delays[(time_into_line >> 1) & 7]);
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -218,7 +218,7 @@ template<Model model> class ConcreteMachine:
|
||||
cycle.operation >= PartialMachineCycle::ReadOpcodeStart &&
|
||||
cycle.operation <= PartialMachineCycle::WriteStart) {
|
||||
|
||||
const HalfCycles delay = video_.last_valid()->access_delay(video_.time_since_flush() + HalfCycles(1));
|
||||
const auto delay = video_.last_valid()->access_delay(video_.time_since_flush());
|
||||
advance(cycle.length + delay);
|
||||
return delay;
|
||||
}
|
||||
@ -269,7 +269,7 @@ template<Model model> class ConcreteMachine:
|
||||
// These all start by loading the address bus, then set MREQ
|
||||
// half a cycle later.
|
||||
if(is_contended_[address >> 14]) {
|
||||
const HalfCycles delay = video_.last_valid()->access_delay(video_.time_since_flush());
|
||||
const auto delay = video_.last_valid()->access_delay(video_.time_since_flush());
|
||||
|
||||
advance(cycle.length + delay);
|
||||
return delay;
|
||||
|
Loading…
Reference in New Issue
Block a user