1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-12-23 20:29:42 +00:00

This is correct from the Enterprise's side of things, I think.

I just need to complete the missing part of JustInTimeActor. After I do some empirical testing of this.
This commit is contained in:
Thomas Harte 2021-06-27 17:24:21 -04:00
parent f2d7b9f6a9
commit 81e9ba5608
2 changed files with 49 additions and 13 deletions

View File

@ -145,13 +145,21 @@ template <class T, class LocalTimeScale = HalfCycles, int multiplier = 1, int di
/// @returns the amount of time since the object was last flushed, in the target time scale.
[[nodiscard]] forceinline TargetTimeScale time_since_flush() const {
// TODO: does this handle conversions properly where TargetTimeScale != LocalTimeScale?
if constexpr (divider == 1) {
return time_since_update_;
}
return TargetTimeScale(time_since_update_.as_integral() / divider);
}
/// @returns the amount of time since the object was last flushed, plus the local time scale @c offset,
/// converted to the target time scale.
[[nodiscard]] forceinline TargetTimeScale time_since_flush(LocalTimeScale offset) const {
if constexpr (divider == 1) {
return time_since_update_ + offset;
}
return TargetTimeScale((time_since_update_ + offset).as_integral() / divider);
}
/// Flushes all accumulated time.
///
/// This does not affect this actor's record of when the next sequence point will occur.
@ -196,6 +204,13 @@ template <class T, class LocalTimeScale = HalfCycles, int multiplier = 1, int di
return rhs >= time_until_event_;
}
/// Indicates the amount of time, in the local time scale, until the first local slot that falls wholly
/// after the amount of time provided in the target time scale.
[[nodiscard]] forceinline LocalTimeScale back_map(TargetTimeScale time) const {
// TODO.
return LocalTimeScale(0);
}
/// Updates this template's record of the next sequence point.
void update_sequence_point() {
if constexpr (has_sequence_points<T>::value) {

View File

@ -221,6 +221,17 @@ template <bool has_disk_controller> class ConcreteMachine:
}
// MARK: - Z80::BusHandler.
forceinline void advance_nick(HalfCycles duration) {
if(nick_ += duration) {
const auto nick = nick_.last_valid();
const bool nick_interrupt_line = nick->get_interrupt_line();
if(nick_interrupt_line && !previous_nick_interrupt_line_) {
set_interrupt(Interrupt::Nick, nick_.last_sequence_point_overrun());
}
previous_nick_interrupt_line_ = nick_interrupt_line;
}
}
forceinline HalfCycles perform_machine_cycle(const CPU::Z80::PartialMachineCycle &cycle) {
using PartialMachineCycle = CPU::Z80::PartialMachineCycle;
const uint16_t address = cycle.address ? *cycle.address : 0x0000;
@ -240,36 +251,46 @@ template <bool has_disk_controller> class ConcreteMachine:
case CPU::Z80::PartialMachineCycle::ReadOpcodeStart:
if(!is_video_[address >> 14] && wait_mode_ != WaitMode::None) {
penalty = HalfCycles(2);
} else {
// Query Nick for the amount of delay that would occur with one cycle left
// in this read opcode.
const auto delay = nick_.last_valid()->get_time_until_z80_slot(nick_.time_since_flush(HalfCycles(2)));
penalty = nick_.back_map(delay);
}
break;
// Video pauses: insert right at the end of the bus cycle.
case CPU::Z80::PartialMachineCycle::ReadOpcode:
case CPU::Z80::PartialMachineCycle::Read:
case CPU::Z80::PartialMachineCycle::Write:
// Ensure all video that should have been collected prior to
// this write has been.
if(is_video_[address >> 14]) {
// TODO.
nick_.flush();
}
[[fallthrough]];
case CPU::Z80::PartialMachineCycle::Read:
if(is_video_[address >> 14]) {
// Get delay, in Nick cycles, for a Z80 access that occurs in 0.5
// cycles from now (i.e. with one cycle left to run).
const auto delay = nick_.last_valid()->get_time_until_z80_slot(nick_.time_since_flush(HalfCycles(1)));
penalty = nick_.back_map(delay);
}
break;
case CPU::Z80::PartialMachineCycle::Input:
case CPU::Z80::PartialMachineCycle::Output: {
if((address & 0xf0) == 0x80) {
// TODO.
// Get delay, in Nick cycles, for a Z80 access that occurs in 0.5
// cycles from now (i.e. with one cycle left to run).
const auto delay = nick_.last_valid()->get_time_until_z80_slot(nick_.time_since_flush(HalfCycles(1)));
penalty = nick_.back_map(delay);
}
}
}
const HalfCycles full_length = cycle.length + penalty;
time_since_audio_update_ += full_length;
if(nick_ += full_length) {
const auto nick = nick_.last_valid();
const bool nick_interrupt_line = nick->get_interrupt_line();
if(nick_interrupt_line && !previous_nick_interrupt_line_) {
set_interrupt(Interrupt::Nick, nick_.last_sequence_point_overrun());
}
previous_nick_interrupt_line_ = nick_interrupt_line;
}
advance_nick(full_length);
// The WD/etc runs at a nominal 8Mhz.
if constexpr (has_disk_controller) {