mirror of
https://github.com/TomHarte/CLK.git
synced 2025-08-09 20:25:19 +00:00
Merge pull request #1410 from TomHarte/CPCLatency
Further improve the CPC side of the CPC:CRTC relationship.
This commit is contained in:
@@ -55,8 +55,6 @@ class InterruptTimer {
|
||||
trailing edge because it is active high.
|
||||
*/
|
||||
inline void signal_hsync() {
|
||||
// printf("count h: %d/%d [%d]\n", timer_, reset_counter_, interrupt_request_);
|
||||
|
||||
// Increment the timer and if it has hit 52 then reset it and
|
||||
// set the interrupt request line to true.
|
||||
++timer_;
|
||||
@@ -81,13 +79,11 @@ class InterruptTimer {
|
||||
|
||||
/// Indicates the leading edge of a new vertical sync.
|
||||
inline void signal_vsync() {
|
||||
// printf("count v\n");
|
||||
reset_counter_ = 2;
|
||||
}
|
||||
|
||||
/// Indicates that an interrupt acknowledge has been received from the Z80.
|
||||
inline void signal_interrupt_acknowledge() {
|
||||
// printf("count IRQA\n");
|
||||
interrupt_request_ = false;
|
||||
timer_ &= ~32;
|
||||
}
|
||||
@@ -104,7 +100,6 @@ class InterruptTimer {
|
||||
|
||||
/// Resets the timer.
|
||||
inline void reset_count() {
|
||||
// printf("count reset\n");
|
||||
timer_ = 0;
|
||||
interrupt_request_ = false;
|
||||
}
|
||||
@@ -190,6 +185,8 @@ class CRTCBusHandler {
|
||||
bus state and determines what output to produce based on the current palette and mode.
|
||||
*/
|
||||
forceinline void perform_bus_cycle(const Motorola::CRTC::BusState &state) {
|
||||
// TODO: there's a one-tick delay on pixel output; incorporate that.
|
||||
|
||||
// The gate array waits 2us to react to the CRTC's vsync signal, and then
|
||||
// caps output at 4us. Since the clock rate is 1Mhz, that's 2 and 4 cycles,
|
||||
// respectively.
|
||||
@@ -240,10 +237,10 @@ class CRTCBusHandler {
|
||||
previous_output_mode_ = output_mode;
|
||||
}
|
||||
|
||||
// increment cycles since state changed
|
||||
// Increment cycles since state changed.
|
||||
cycles_++;
|
||||
|
||||
// collect some more pixels if output is ongoing
|
||||
// Collect some more pixels if output is ongoing.
|
||||
if(previous_output_mode_ == OutputMode::Pixels) {
|
||||
if(!pixel_data_) {
|
||||
pixel_pointer_ = pixel_data_ = crt_.begin_data(320, 8);
|
||||
@@ -300,17 +297,8 @@ class CRTCBusHandler {
|
||||
}
|
||||
}
|
||||
|
||||
// Notify a leading hsync edge to the interrupt timer.
|
||||
// Per Interrupts in the CPC: "to be confirmed: does gate array count positive or negative edge transitions of HSYNC signal?";
|
||||
// if you take it as given that display mode is latched as a result of hsync then Pipe Mania seems to imply that the count
|
||||
// occurs on a leading edge and the mode lock on a trailing.
|
||||
if(!was_hsync_ && state.hsync) {
|
||||
interrupt_timer_.signal_hsync();
|
||||
}
|
||||
|
||||
// Check for a trailing CRTC hsync; if one occurred then that's the trigger potentially to change modes.
|
||||
if(was_hsync_ && !state.hsync) {
|
||||
if(mode_ != next_mode_) {
|
||||
// Latch mode four cycles after HSYNC was signalled, if still active.
|
||||
if(cycles_into_hsync_ == 4 && mode_ != next_mode_) {
|
||||
mode_ = next_mode_;
|
||||
switch(mode_) {
|
||||
default:
|
||||
@@ -320,14 +308,17 @@ class CRTCBusHandler {
|
||||
}
|
||||
build_mode_table();
|
||||
}
|
||||
}
|
||||
|
||||
// check for a leading vsync; that also needs to be communicated to the interrupt timer
|
||||
// For the interrupt timer: notify the leading edge of vertical sync and the
|
||||
// trailing edge of horizontal sync.
|
||||
if(!was_vsync_ && state.vsync) {
|
||||
interrupt_timer_.signal_vsync();
|
||||
}
|
||||
if(was_hsync_ && !state.hsync) {
|
||||
interrupt_timer_.signal_hsync();
|
||||
}
|
||||
|
||||
// update current state for edge detection next time around
|
||||
// Update current state for edge detection next time around.
|
||||
was_vsync_ = state.vsync;
|
||||
was_hsync_ = state.hsync;
|
||||
}
|
||||
@@ -858,6 +849,9 @@ class ConcreteMachine:
|
||||
clock_offset_ = (clock_offset_ + cycle.length) & HalfCycles(7);
|
||||
z80_.set_wait_line(clock_offset_ >= HalfCycles(2));
|
||||
|
||||
// Float this out as a lambda to allow easy repositioning relative to the CPU activity;
|
||||
// for now this is largely experimental.
|
||||
const auto update_subsystems = [&] {
|
||||
// Update the CRTC once every eight half cycles; aiming for half-cycle 4 as
|
||||
// per the initial seed to the crtc_counter_, but any time in the final four
|
||||
// will do as it's safe to conclude that nobody else has touched video RAM
|
||||
@@ -884,10 +878,10 @@ class ConcreteMachine:
|
||||
|
||||
// Update typing activity.
|
||||
if(typer_) typer_->run_for(cycle.length);
|
||||
};
|
||||
|
||||
// Stop now if no action is strictly required.
|
||||
if(!cycle.is_terminal()) return HalfCycles(0);
|
||||
|
||||
// Continue only if action strictly required.
|
||||
if(cycle.is_terminal()) {
|
||||
uint16_t address = cycle.address ? *cycle.address : 0x0000;
|
||||
switch(cycle.operation) {
|
||||
case CPU::Z80::PartialMachineCycle::ReadOpcode:
|
||||
@@ -1073,8 +1067,11 @@ class ConcreteMachine:
|
||||
default: break;
|
||||
}
|
||||
|
||||
// Check whether the interrupt signal has changed the other way.
|
||||
// Check whether the interrupt signal has changed due to CPU intervention.
|
||||
if(interrupt_timer_.request_has_changed()) z80_.set_interrupt_line(interrupt_timer_.get_request());
|
||||
}
|
||||
|
||||
update_subsystems();
|
||||
|
||||
// This implementation doesn't use time-stuffing; once in-phase waits won't be longer
|
||||
// than a single cycle so there's no real performance benefit to trying to find the
|
||||
|
@@ -43,9 +43,6 @@ struct ScanTarget: public Outputs::Display::ScanTarget {
|
||||
const int src_pixels = scan_.end_points[1].data_offset - scan_.end_points[0].data_offset;
|
||||
const int dst_pixels = (scan_.end_points[1].x - scan_.end_points[0].x) / WidthDivider;
|
||||
|
||||
const int step = (src_pixels << 16) / dst_pixels;
|
||||
int position = 0;
|
||||
|
||||
const auto x1 = scan_.end_points[0].x / WidthDivider;
|
||||
const auto x2 = scan_.end_points[1].x / WidthDivider;
|
||||
|
||||
@@ -53,10 +50,16 @@ struct ScanTarget: public Outputs::Display::ScanTarget {
|
||||
if(x_ < x1) {
|
||||
std::fill(&line[x_], &line[x1], 0);
|
||||
}
|
||||
|
||||
if(x2 != x1) {
|
||||
const int step = (src_pixels << 16) / dst_pixels;
|
||||
int position = 0;
|
||||
|
||||
for(int x = x1; x < x2; x++) {
|
||||
line[x] = data_[position >> 16];
|
||||
position += step;
|
||||
}
|
||||
}
|
||||
x_ = x2;
|
||||
}
|
||||
void announce(Event event, bool, const Scan::EndPoint &, uint8_t) override {
|
||||
|
Reference in New Issue
Block a user