1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-06-25 18:30:07 +00:00

Makes the TMS a sequence-point-generating JustInTimeActor.

This commit is contained in:
Thomas Harte 2021-04-05 21:02:37 -04:00
parent bd1ea5740a
commit 1266bbb224
7 changed files with 33 additions and 40 deletions

View File

@ -10,7 +10,9 @@
#define ClockReceiver_hpp
#include "ForceInline.hpp"
#include <cstdint>
#include <limits>
/*
Informal pattern for all classes that run from a clock cycle:
@ -176,6 +178,9 @@ class Cycles: public WrappedInt<Cycles> {
public:
forceinline constexpr Cycles(IntType l) noexcept : WrappedInt<Cycles>(l) {}
forceinline constexpr Cycles() noexcept : WrappedInt<Cycles>() {}
forceinline static constexpr Cycles max() {
return Cycles(std::numeric_limits<IntType>::max());
}
private:
friend WrappedInt;
@ -195,6 +200,9 @@ class HalfCycles: public WrappedInt<HalfCycles> {
public:
forceinline constexpr HalfCycles(IntType l) noexcept : WrappedInt<HalfCycles>(l) {}
forceinline constexpr HalfCycles() noexcept : WrappedInt<HalfCycles>() {}
forceinline static constexpr HalfCycles max() {
return HalfCycles(std::numeric_limits<IntType>::max());
}
forceinline constexpr HalfCycles(const Cycles &cycles) noexcept : WrappedInt<HalfCycles>(cycles.as_integral() * 2) {}

View File

@ -105,7 +105,9 @@ template <class T, class LocalTimeScale = HalfCycles, int multiplier = 1, int di
if constexpr (has_sequence_points<T>::value) {
time_until_event_ -= rhs;
if(time_until_event_ <= LocalTimeScale(0)) {
time_overrun_ = time_until_event_;
flush();
update_sequence_point();
return true;
}
}
@ -174,6 +176,12 @@ template <class T, class LocalTimeScale = HalfCycles, int multiplier = 1, int di
return did_flush;
}
/// @returns a number in the range [-max, 0] indicating the offset of the most recent sequence
/// point from the final time at the end of the += that triggered the sequence point.
[[nodiscard]] forceinline LocalTimeScale last_sequence_point_overrun() {
return time_overrun_;
}
/// @returns the number of cycles until the next sequence-point-based flush, if the embedded object
/// supports sequence points; @c LocalTimeScale() otherwise.
[[nodiscard]] LocalTimeScale cycles_until_implicit_flush() const {
@ -203,7 +211,7 @@ template <class T, class LocalTimeScale = HalfCycles, int multiplier = 1, int di
private:
T object_;
LocalTimeScale time_since_update_, time_until_event_;
LocalTimeScale time_since_update_, time_until_event_, time_overrun_;
bool is_flushed_ = true;
bool did_flush_ = false;

View File

@ -708,9 +708,9 @@ HalfCycles Base::half_cycles_before_internal_cycles(int internal_cycles) {
return HalfCycles(((internal_cycles << 2) + (2 - cycles_error_)) / 3);
}
HalfCycles TMS9918::get_time_until_interrupt() {
if(!generate_interrupts_ && !enable_line_interrupts_) return HalfCycles(-1);
if(get_interrupt_line()) return HalfCycles(0);
HalfCycles TMS9918::get_next_sequence_point() {
if(!generate_interrupts_ && !enable_line_interrupts_) return HalfCycles::max();
if(get_interrupt_line()) return HalfCycles::max();
// Calculate the amount of time until the next end-of-frame interrupt.
const int frame_length = 342 * mode_timing_.total_lines;

View File

@ -75,13 +75,13 @@ class TMS9918: public Base {
void latch_horizontal_counter();
/*!
Returns the amount of time until @c get_interrupt_line would next return true if
Returns the amount of time until @c get_interrupt_line would next change if
there are no interceding calls to @c write or to @c read.
If get_interrupt_line is true now, returns zero. If get_interrupt_line would
never return true, returns -1.
If get_interrupt_line is true now of if get_interrupt_line would
never return true, returns HalfCycles::max().
*/
HalfCycles get_time_until_interrupt();
HalfCycles get_next_sequence_point();
/*!
Returns the amount of time until the nominated line interrupt position is

View File

@ -215,7 +215,9 @@ class ConcreteMachine:
}
const HalfCycles length = cycle.length + penalty;
vdp_ += length;
if(vdp_ += length) {
z80_.set_non_maskable_interrupt_line(vdp_->get_interrupt_line());
}
time_since_sn76489_update_ += length;
// Act only if necessary.
@ -263,7 +265,6 @@ class ConcreteMachine:
case 5:
*cycle.value = vdp_->read(address);
z80_.set_non_maskable_interrupt_line(vdp_->get_interrupt_line());
time_until_interrupt_ = vdp_->get_time_until_interrupt();
break;
case 7: {
@ -304,7 +305,6 @@ class ConcreteMachine:
case 5:
vdp_->write(address, *cycle.value);
z80_.set_non_maskable_interrupt_line(vdp_->get_interrupt_line());
time_until_interrupt_ = vdp_->get_time_until_interrupt();
break;
case 7:
@ -341,13 +341,6 @@ class ConcreteMachine:
}
}
if(time_until_interrupt_ > 0) {
time_until_interrupt_ -= length;
if(time_until_interrupt_ <= HalfCycles(0)) {
z80_.set_non_maskable_interrupt_line(true, time_until_interrupt_);
}
}
return penalty;
}
@ -408,7 +401,6 @@ class ConcreteMachine:
bool joysticks_in_keypad_mode_ = false;
HalfCycles time_since_sn76489_update_;
HalfCycles time_until_interrupt_;
Analyser::Dynamic::ConfidenceCounter confidence_counter_;
int pc_zero_accesses_ = 0;

View File

@ -419,7 +419,9 @@ class ConcreteMachine:
// but otherwise runs without pause.
const HalfCycles addition((cycle.operation == CPU::Z80::PartialMachineCycle::ReadOpcode) ? 2 : 0);
const HalfCycles total_length = addition + cycle.length;
vdp_ += total_length;
if(vdp_ += total_length) {
z80_.set_interrupt_line(vdp_->get_interrupt_line(), vdp_.last_sequence_point_overrun());
}
time_since_ay_update_ += total_length;
memory_slots_[0].cycles_since_update += total_length;
memory_slots_[1].cycles_since_update += total_length;
@ -520,7 +522,6 @@ class ConcreteMachine:
case 0x98: case 0x99:
*cycle.value = vdp_->read(address);
z80_.set_interrupt_line(vdp_->get_interrupt_line());
time_until_interrupt_ = vdp_->get_time_until_interrupt();
break;
case 0xa2:
@ -545,7 +546,6 @@ class ConcreteMachine:
case 0x98: case 0x99:
vdp_->write(address, *cycle.value);
z80_.set_interrupt_line(vdp_->get_interrupt_line());
time_until_interrupt_ = vdp_->get_time_until_interrupt();
break;
case 0xa0: case 0xa1:
@ -606,12 +606,6 @@ class ConcreteMachine:
if(!tape_player_is_sleeping_)
tape_player_.run_for(int(cycle.length.as_integral()));
if(time_until_interrupt_ > 0) {
time_until_interrupt_ -= total_length;
if(time_until_interrupt_ <= HalfCycles(0)) {
z80_.set_interrupt_line(true, time_until_interrupt_);
}
}
return addition;
}
@ -785,7 +779,6 @@ class ConcreteMachine:
uint8_t unpopulated_[8192];
HalfCycles time_since_ay_update_;
HalfCycles time_until_interrupt_;
uint8_t key_states_[16];
int selected_key_line_ = 0;

View File

@ -214,7 +214,9 @@ class ConcreteMachine:
}
forceinline HalfCycles perform_machine_cycle(const CPU::Z80::PartialMachineCycle &cycle) {
vdp_ += cycle.length;
if(vdp_ += cycle.length) {
z80_.set_interrupt_line(vdp_->get_interrupt_line(), vdp_.last_sequence_point_overrun());
}
time_since_sn76489_update_ += cycle.length;
if(cycle.is_terminal()) {
@ -266,7 +268,6 @@ class ConcreteMachine:
case 0x80: case 0x81:
*cycle.value = vdp_->read(address);
z80_.set_interrupt_line(vdp_->get_interrupt_line());
time_until_interrupt_ = vdp_->get_time_until_interrupt();
break;
case 0xc0: {
if(memory_control_ & 0x4) {
@ -330,7 +331,6 @@ class ConcreteMachine:
case 0x80: case 0x81: // i.e. ports 0x800xbf.
vdp_->write(address, *cycle.value);
z80_.set_interrupt_line(vdp_->get_interrupt_line());
time_until_interrupt_ = vdp_->get_time_until_interrupt();
break;
case 0xc1: case 0xc0: // i.e. ports 0xc00xff.
if(has_fm_audio_) {
@ -374,13 +374,6 @@ class ConcreteMachine:
}
}
if(time_until_interrupt_ > 0) {
time_until_interrupt_ -= cycle.length;
if(time_until_interrupt_ <= HalfCycles(0)) {
z80_.set_interrupt_line(true, time_until_interrupt_);
}
}
// The pause button is debounced and takes effect only one line before pixels
// begin; time_until_debounce_ keeps track of the time until then.
time_until_debounce_ -= cycle.length;
@ -505,7 +498,6 @@ class ConcreteMachine:
bool reset_is_pressed_ = false, pause_is_pressed_ = false;
HalfCycles time_since_sn76489_update_;
HalfCycles time_until_interrupt_;
HalfCycles time_until_debounce_;
uint8_t ram_[8*1024];