mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-23 03:32:32 +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:
parent
f2d7b9f6a9
commit
81e9ba5608
@ -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.
|
/// @returns the amount of time since the object was last flushed, in the target time scale.
|
||||||
[[nodiscard]] forceinline TargetTimeScale time_since_flush() const {
|
[[nodiscard]] forceinline TargetTimeScale time_since_flush() const {
|
||||||
// TODO: does this handle conversions properly where TargetTimeScale != LocalTimeScale?
|
|
||||||
if constexpr (divider == 1) {
|
if constexpr (divider == 1) {
|
||||||
return time_since_update_;
|
return time_since_update_;
|
||||||
}
|
}
|
||||||
return TargetTimeScale(time_since_update_.as_integral() / divider);
|
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.
|
/// Flushes all accumulated time.
|
||||||
///
|
///
|
||||||
/// This does not affect this actor's record of when the next sequence point will occur.
|
/// 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_;
|
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.
|
/// Updates this template's record of the next sequence point.
|
||||||
void update_sequence_point() {
|
void update_sequence_point() {
|
||||||
if constexpr (has_sequence_points<T>::value) {
|
if constexpr (has_sequence_points<T>::value) {
|
||||||
|
@ -221,6 +221,17 @@ template <bool has_disk_controller> class ConcreteMachine:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Z80::BusHandler.
|
// 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) {
|
forceinline HalfCycles perform_machine_cycle(const CPU::Z80::PartialMachineCycle &cycle) {
|
||||||
using PartialMachineCycle = CPU::Z80::PartialMachineCycle;
|
using PartialMachineCycle = CPU::Z80::PartialMachineCycle;
|
||||||
const uint16_t address = cycle.address ? *cycle.address : 0x0000;
|
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:
|
case CPU::Z80::PartialMachineCycle::ReadOpcodeStart:
|
||||||
if(!is_video_[address >> 14] && wait_mode_ != WaitMode::None) {
|
if(!is_video_[address >> 14] && wait_mode_ != WaitMode::None) {
|
||||||
penalty = HalfCycles(2);
|
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;
|
break;
|
||||||
|
|
||||||
// Video pauses: insert right at the end of the bus cycle.
|
// 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:
|
case CPU::Z80::PartialMachineCycle::Write:
|
||||||
|
// Ensure all video that should have been collected prior to
|
||||||
|
// this write has been.
|
||||||
if(is_video_[address >> 14]) {
|
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;
|
break;
|
||||||
|
|
||||||
case CPU::Z80::PartialMachineCycle::Input:
|
case CPU::Z80::PartialMachineCycle::Input:
|
||||||
case CPU::Z80::PartialMachineCycle::Output: {
|
case CPU::Z80::PartialMachineCycle::Output: {
|
||||||
if((address & 0xf0) == 0x80) {
|
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;
|
const HalfCycles full_length = cycle.length + penalty;
|
||||||
time_since_audio_update_ += full_length;
|
time_since_audio_update_ += full_length;
|
||||||
if(nick_ += full_length) {
|
advance_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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The WD/etc runs at a nominal 8Mhz.
|
// The WD/etc runs at a nominal 8Mhz.
|
||||||
if constexpr (has_disk_controller) {
|
if constexpr (has_disk_controller) {
|
||||||
|
Loading…
Reference in New Issue
Block a user