mirror of
https://github.com/TomHarte/CLK.git
synced 2024-10-19 14:25:35 +00:00
Merge pull request #633 from TomHarte/Deferrer
Starts to formalise just-in-time handling
This commit is contained in:
commit
bba34b28b8
@ -149,10 +149,12 @@ template <class T> class WrappedInt {
|
|||||||
Flushes the value in @c this. The current value is returned, and the internal value
|
Flushes the value in @c this. The current value is returned, and the internal value
|
||||||
is reset to zero.
|
is reset to zero.
|
||||||
*/
|
*/
|
||||||
forceinline T flush() {
|
template <typename Result> Result flush() {
|
||||||
T result(length_);
|
// Jiggery pokery here; switching to function overloading avoids
|
||||||
length_ = 0;
|
// the namespace-level requirement for template specialisation.
|
||||||
return result;
|
Result r;
|
||||||
|
static_cast<T *>(this)->fill(r);
|
||||||
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
// operator int() is deliberately not provided, to avoid accidental subtitution of
|
// operator int() is deliberately not provided, to avoid accidental subtitution of
|
||||||
@ -168,6 +170,13 @@ class Cycles: public WrappedInt<Cycles> {
|
|||||||
forceinline constexpr Cycles(int l) noexcept : WrappedInt<Cycles>(l) {}
|
forceinline constexpr Cycles(int l) noexcept : WrappedInt<Cycles>(l) {}
|
||||||
forceinline constexpr Cycles() noexcept : WrappedInt<Cycles>() {}
|
forceinline constexpr Cycles() noexcept : WrappedInt<Cycles>() {}
|
||||||
forceinline constexpr Cycles(const Cycles &cycles) noexcept : WrappedInt<Cycles>(cycles.length_) {}
|
forceinline constexpr Cycles(const Cycles &cycles) noexcept : WrappedInt<Cycles>(cycles.length_) {}
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend WrappedInt;
|
||||||
|
void fill(Cycles &result) {
|
||||||
|
result.length_ = length_;
|
||||||
|
length_ = 0;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Describes an integer number of half cycles: single clock signal transitions.
|
/// Describes an integer number of half cycles: single clock signal transitions.
|
||||||
@ -176,7 +185,7 @@ class HalfCycles: public WrappedInt<HalfCycles> {
|
|||||||
forceinline constexpr HalfCycles(int l) noexcept : WrappedInt<HalfCycles>(l) {}
|
forceinline constexpr HalfCycles(int l) noexcept : WrappedInt<HalfCycles>(l) {}
|
||||||
forceinline constexpr HalfCycles() noexcept : WrappedInt<HalfCycles>() {}
|
forceinline constexpr HalfCycles() noexcept : WrappedInt<HalfCycles>() {}
|
||||||
|
|
||||||
forceinline constexpr HalfCycles(const Cycles cycles) noexcept : WrappedInt<HalfCycles>(cycles.as_int() * 2) {}
|
forceinline constexpr HalfCycles(const Cycles &cycles) noexcept : WrappedInt<HalfCycles>(cycles.as_int() * 2) {}
|
||||||
forceinline constexpr HalfCycles(const HalfCycles &half_cycles) noexcept : WrappedInt<HalfCycles>(half_cycles.length_) {}
|
forceinline constexpr HalfCycles(const HalfCycles &half_cycles) noexcept : WrappedInt<HalfCycles>(half_cycles.length_) {}
|
||||||
|
|
||||||
/// @returns The number of whole cycles completely covered by this span of half cycles.
|
/// @returns The number of whole cycles completely covered by this span of half cycles.
|
||||||
@ -184,20 +193,6 @@ class HalfCycles: public WrappedInt<HalfCycles> {
|
|||||||
return Cycles(length_ >> 1);
|
return Cycles(length_ >> 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Flushes the whole cycles in @c this, subtracting that many from the total stored here.
|
|
||||||
forceinline Cycles flush_cycles() {
|
|
||||||
Cycles result(length_ >> 1);
|
|
||||||
length_ &= 1;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Flushes the half cycles in @c this, returning the number stored and setting this total to zero.
|
|
||||||
forceinline HalfCycles flush() {
|
|
||||||
HalfCycles result(length_);
|
|
||||||
length_ = 0;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Severs from @c this the effect of dividing by @c divisor; @c this will end up with
|
Severs from @c this the effect of dividing by @c divisor; @c this will end up with
|
||||||
the value of @c this modulo @c divisor and @c divided by @c divisor is returned.
|
the value of @c this modulo @c divisor and @c divided by @c divisor is returned.
|
||||||
@ -208,8 +203,23 @@ class HalfCycles: public WrappedInt<HalfCycles> {
|
|||||||
length_ %= half_divisor.length_;
|
length_ %= half_divisor.length_;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend WrappedInt;
|
||||||
|
void fill(Cycles &result) {
|
||||||
|
result = Cycles(length_ >> 1);
|
||||||
|
length_ &= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fill(HalfCycles &result) {
|
||||||
|
result.length_ = length_;
|
||||||
|
length_ = 0;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Create a specialisation of WrappedInt::flush for converting HalfCycles to Cycles
|
||||||
|
// without losing the fractional part.
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
If a component implements only run_for(Cycles), an owner can wrap it in HalfClockReceiver
|
If a component implements only run_for(Cycles), an owner can wrap it in HalfClockReceiver
|
||||||
automatically to gain run_for(HalfCycles).
|
automatically to gain run_for(HalfCycles).
|
||||||
@ -220,7 +230,7 @@ template <class T> class HalfClockReceiver: public T {
|
|||||||
|
|
||||||
forceinline void run_for(const HalfCycles half_cycles) {
|
forceinline void run_for(const HalfCycles half_cycles) {
|
||||||
half_cycles_ += half_cycles;
|
half_cycles_ += half_cycles;
|
||||||
T::run_for(half_cycles_.flush_cycles());
|
T::run_for(half_cycles_.flush<Cycles>());
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -1,26 +1,26 @@
|
|||||||
//
|
//
|
||||||
// ClockDeferrer.hpp
|
// DeferredQueue.hpp
|
||||||
// Clock Signal
|
// Clock Signal
|
||||||
//
|
//
|
||||||
// Created by Thomas Harte on 23/08/2018.
|
// Created by Thomas Harte on 23/08/2018.
|
||||||
// Copyright © 2018 Thomas Harte. All rights reserved.
|
// Copyright © 2018 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef ClockDeferrer_h
|
#ifndef DeferredQueue_h
|
||||||
#define ClockDeferrer_h
|
#define DeferredQueue_h
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
A ClockDeferrer maintains a list of ordered actions and the times at which
|
A DeferredQueue maintains a list of ordered actions and the times at which
|
||||||
they should happen, and divides a total execution period up into the portions
|
they should happen, and divides a total execution period up into the portions
|
||||||
that occur between those actions, triggering each action when it is reached.
|
that occur between those actions, triggering each action when it is reached.
|
||||||
*/
|
*/
|
||||||
template <typename TimeUnit> class ClockDeferrer {
|
template <typename TimeUnit> class DeferredQueue {
|
||||||
public:
|
public:
|
||||||
/// Constructs a ClockDeferrer that will call target(period) in between deferred actions.
|
/// Constructs a DeferredQueue that will call target(period) in between deferred actions.
|
||||||
ClockDeferrer(std::function<void(TimeUnit)> &&target) : target_(std::move(target)) {}
|
DeferredQueue(std::function<void(TimeUnit)> &&target) : target_(std::move(target)) {}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Schedules @c action to occur in @c delay units of time.
|
Schedules @c action to occur in @c delay units of time.
|
||||||
@ -79,4 +79,4 @@ template <typename TimeUnit> class ClockDeferrer {
|
|||||||
std::vector<DeferredAction> pending_actions_;
|
std::vector<DeferredAction> pending_actions_;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* ClockDeferrer_h */
|
#endif /* DeferredQueue_h */
|
110
ClockReceiver/JustInTime.hpp
Normal file
110
ClockReceiver/JustInTime.hpp
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
//
|
||||||
|
// JustInTime.hpp
|
||||||
|
// Clock Signal
|
||||||
|
//
|
||||||
|
// Created by Thomas Harte on 28/07/2019.
|
||||||
|
// Copyright © 2019 Thomas Harte. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef JustInTime_h
|
||||||
|
#define JustInTime_h
|
||||||
|
|
||||||
|
#include "../Concurrency/AsyncTaskQueue.hpp"
|
||||||
|
|
||||||
|
/*!
|
||||||
|
A JustInTimeActor holds (i) an embedded object with a run_for method; and (ii) an amount
|
||||||
|
of time since run_for was last called.
|
||||||
|
|
||||||
|
Time can be added using the += operator. The -> operator can be used to access the
|
||||||
|
embedded object. All time accumulated will be pushed to object before the pointer is returned.
|
||||||
|
|
||||||
|
Machines that accumulate HalfCycle time but supply to a Cycle-counted device may supply a
|
||||||
|
separate @c TargetTimeScale at template declaration.
|
||||||
|
*/
|
||||||
|
template <class T, class LocalTimeScale, class TargetTimeScale = LocalTimeScale> class JustInTimeActor {
|
||||||
|
public:
|
||||||
|
/// Constructs a new JustInTimeActor using the same construction arguments as the included object.
|
||||||
|
template<typename... Args> JustInTimeActor(Args&&... args) : object_(std::forward<Args>(args)...) {}
|
||||||
|
|
||||||
|
/// Adds time to the actor.
|
||||||
|
inline void operator += (const LocalTimeScale &rhs) {
|
||||||
|
time_since_update_ += rhs;
|
||||||
|
is_flushed_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Flushes all accumulated time and returns a pointer to the included object.
|
||||||
|
inline T *operator->() {
|
||||||
|
flush();
|
||||||
|
return &object_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a pointer to the included object without flushing time.
|
||||||
|
inline T *last_valid() {
|
||||||
|
return &object_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Flushes all accumulated time.
|
||||||
|
inline void flush() {
|
||||||
|
if(!is_flushed_) object_.run_for(time_since_update_.template flush<TargetTimeScale>());
|
||||||
|
is_flushed_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
T object_;
|
||||||
|
LocalTimeScale time_since_update_;
|
||||||
|
bool is_flushed_ = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
A AsyncJustInTimeActor acts like a JustInTimeActor but additionally contains an AsyncTaskQueue.
|
||||||
|
Any time the amount of accumulated time crosses a threshold provided at construction time,
|
||||||
|
the object will be updated on the AsyncTaskQueue.
|
||||||
|
*/
|
||||||
|
template <class T, class LocalTimeScale, class TargetTimeScale = LocalTimeScale> class AsyncJustInTimeActor {
|
||||||
|
public:
|
||||||
|
/// Constructs a new AsyncJustInTimeActor using the same construction arguments as the included object.
|
||||||
|
template<typename... Args> AsyncJustInTimeActor(TargetTimeScale threshold, Args&&... args) :
|
||||||
|
object_(std::forward<Args>(args)...),
|
||||||
|
threshold_(threshold) {}
|
||||||
|
|
||||||
|
/// Adds time to the actor.
|
||||||
|
inline void operator += (const LocalTimeScale &rhs) {
|
||||||
|
time_since_update_ += rhs;
|
||||||
|
if(time_since_update_ >= threshold_) {
|
||||||
|
time_since_update_ -= threshold_;
|
||||||
|
task_queue_.enqueue([this] () {
|
||||||
|
object_.run_for(threshold_);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
is_flushed_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Flushes all accumulated time and returns a pointer to the included object.
|
||||||
|
inline T *operator->() {
|
||||||
|
flush();
|
||||||
|
return &object_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a pointer to the included object without flushing time.
|
||||||
|
inline T *last_valid() {
|
||||||
|
return &object_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Flushes all accumulated time.
|
||||||
|
inline void flush() {
|
||||||
|
if(!is_flushed_) {
|
||||||
|
task_queue_.flush();
|
||||||
|
object_.run_for(time_since_update_.template flush<TargetTimeScale>());
|
||||||
|
is_flushed_ = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
T object_;
|
||||||
|
LocalTimeScale time_since_update_;
|
||||||
|
TargetTimeScale threshold_;
|
||||||
|
bool is_flushed_ = true;
|
||||||
|
Concurrency::AsyncTaskQueue task_queue_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* JustInTime_h */
|
@ -38,7 +38,7 @@ template <typename T> void MOS6522<T>::set_register(int address, uint8_t value)
|
|||||||
// Store locally and communicate outwards.
|
// Store locally and communicate outwards.
|
||||||
registers_.output[1] = value;
|
registers_.output[1] = value;
|
||||||
|
|
||||||
bus_handler_.run_for(time_since_bus_handler_call_.flush());
|
bus_handler_.run_for(time_since_bus_handler_call_.flush<HalfCycles>());
|
||||||
bus_handler_.set_port_output(Port::B, value, registers_.data_direction[1]);
|
bus_handler_.set_port_output(Port::B, value, registers_.data_direction[1]);
|
||||||
|
|
||||||
registers_.interrupt_flags &= ~(InterruptFlag::CB1ActiveEdge | ((registers_.peripheral_control&0x20) ? 0 : InterruptFlag::CB2ActiveEdge));
|
registers_.interrupt_flags &= ~(InterruptFlag::CB1ActiveEdge | ((registers_.peripheral_control&0x20) ? 0 : InterruptFlag::CB2ActiveEdge));
|
||||||
@ -48,7 +48,7 @@ template <typename T> void MOS6522<T>::set_register(int address, uint8_t value)
|
|||||||
case 0x1: // Write Port A.
|
case 0x1: // Write Port A.
|
||||||
registers_.output[0] = value;
|
registers_.output[0] = value;
|
||||||
|
|
||||||
bus_handler_.run_for(time_since_bus_handler_call_.flush());
|
bus_handler_.run_for(time_since_bus_handler_call_.flush<HalfCycles>());
|
||||||
bus_handler_.set_port_output(Port::A, value, registers_.data_direction[0]);
|
bus_handler_.set_port_output(Port::A, value, registers_.data_direction[0]);
|
||||||
|
|
||||||
if(handshake_modes_[1] != HandshakeMode::None) {
|
if(handshake_modes_[1] != HandshakeMode::None) {
|
||||||
@ -205,7 +205,7 @@ template <typename T> uint8_t MOS6522<T>::get_register(int address) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename T> uint8_t MOS6522<T>::get_port_input(Port port, uint8_t output_mask, uint8_t output) {
|
template <typename T> uint8_t MOS6522<T>::get_port_input(Port port, uint8_t output_mask, uint8_t output) {
|
||||||
bus_handler_.run_for(time_since_bus_handler_call_.flush());
|
bus_handler_.run_for(time_since_bus_handler_call_.flush<HalfCycles>());
|
||||||
const uint8_t input = bus_handler_.get_port_input(port);
|
const uint8_t input = bus_handler_.get_port_input(port);
|
||||||
return (input & ~output_mask) | (output & output_mask);
|
return (input & ~output_mask) | (output & output_mask);
|
||||||
}
|
}
|
||||||
@ -220,7 +220,7 @@ template <typename T> void MOS6522<T>::reevaluate_interrupts() {
|
|||||||
if(new_interrupt_status != last_posted_interrupt_status_) {
|
if(new_interrupt_status != last_posted_interrupt_status_) {
|
||||||
last_posted_interrupt_status_ = new_interrupt_status;
|
last_posted_interrupt_status_ = new_interrupt_status;
|
||||||
|
|
||||||
bus_handler_.run_for(time_since_bus_handler_call_.flush());
|
bus_handler_.run_for(time_since_bus_handler_call_.flush<HalfCycles>());
|
||||||
bus_handler_.set_interrupt_status(new_interrupt_status);
|
bus_handler_.set_interrupt_status(new_interrupt_status);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -338,7 +338,7 @@ template <typename T> void MOS6522<T>::do_phase1() {
|
|||||||
// Determine whether to toggle PB7.
|
// Determine whether to toggle PB7.
|
||||||
if(registers_.auxiliary_control&0x80) {
|
if(registers_.auxiliary_control&0x80) {
|
||||||
registers_.output[1] ^= 0x80;
|
registers_.output[1] ^= 0x80;
|
||||||
bus_handler_.run_for(time_since_bus_handler_call_.flush());
|
bus_handler_.run_for(time_since_bus_handler_call_.flush<HalfCycles>());
|
||||||
bus_handler_.set_port_output(Port::B, registers_.output[1], registers_.data_direction[1]);
|
bus_handler_.set_port_output(Port::B, registers_.output[1], registers_.data_direction[1]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -369,7 +369,7 @@ template <typename T> void MOS6522<T>::run_for(const HalfCycles half_cycles) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename T> void MOS6522<T>::flush() {
|
template <typename T> void MOS6522<T>::flush() {
|
||||||
bus_handler_.run_for(time_since_bus_handler_call_.flush());
|
bus_handler_.run_for(time_since_bus_handler_call_.flush<HalfCycles>());
|
||||||
bus_handler_.flush();
|
bus_handler_.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -422,7 +422,7 @@ template <typename T> void MOS6522<T>::set_control_line_output(Port port, Line l
|
|||||||
control_outputs_[port].lines[line] = value;
|
control_outputs_[port].lines[line] = value;
|
||||||
|
|
||||||
if(value != LineState::Input) {
|
if(value != LineState::Input) {
|
||||||
bus_handler_.run_for(time_since_bus_handler_call_.flush());
|
bus_handler_.run_for(time_since_bus_handler_call_.flush<HalfCycles>());
|
||||||
bus_handler_.set_control_line_output(port, line, value != LineState::Off);
|
bus_handler_.set_control_line_output(port, line, value != LineState::Off);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -77,7 +77,7 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
|
|||||||
Cycles cycles_since_video_update_;
|
Cycles cycles_since_video_update_;
|
||||||
|
|
||||||
void update_video() {
|
void update_video() {
|
||||||
video_.run_for(cycles_since_video_update_.flush());
|
video_.run_for(cycles_since_video_update_.flush<Cycles>());
|
||||||
}
|
}
|
||||||
static const int audio_divider = 8;
|
static const int audio_divider = 8;
|
||||||
void update_audio() {
|
void update_audio() {
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
#include "../../../Outputs/CRT/CRT.hpp"
|
#include "../../../Outputs/CRT/CRT.hpp"
|
||||||
#include "../../../ClockReceiver/ClockReceiver.hpp"
|
#include "../../../ClockReceiver/ClockReceiver.hpp"
|
||||||
#include "../../../ClockReceiver/ClockDeferrer.hpp"
|
#include "../../../ClockReceiver/DeferredQueue.hpp"
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@ -251,8 +251,8 @@ class VideoBase {
|
|||||||
*/
|
*/
|
||||||
void output_fat_low_resolution(uint8_t *target, const uint8_t *source, size_t length, int column, int row) const;
|
void output_fat_low_resolution(uint8_t *target, const uint8_t *source, size_t length, int column, int row) const;
|
||||||
|
|
||||||
// Maintain a ClockDeferrer for delayed mode switches.
|
// Maintain a DeferredQueue for delayed mode switches.
|
||||||
ClockDeferrer<Cycles> deferrer_;
|
DeferredQueue<Cycles> deferrer_;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <class BusHandler, bool is_iie> class Video: public VideoBase {
|
template <class BusHandler, bool is_iie> class Video: public VideoBase {
|
||||||
|
@ -24,7 +24,7 @@ struct DeferredAudio {
|
|||||||
DeferredAudio() : audio(queue), speaker(audio) {}
|
DeferredAudio() : audio(queue), speaker(audio) {}
|
||||||
|
|
||||||
void flush() {
|
void flush() {
|
||||||
speaker.run_for(queue, time_since_update.flush_cycles());
|
speaker.run_for(queue, time_since_update.flush<Cycles>());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -24,6 +24,8 @@
|
|||||||
#include "../../../Inputs/QuadratureMouse/QuadratureMouse.hpp"
|
#include "../../../Inputs/QuadratureMouse/QuadratureMouse.hpp"
|
||||||
#include "../../../Outputs/Log.hpp"
|
#include "../../../Outputs/Log.hpp"
|
||||||
|
|
||||||
|
#include "../../../ClockReceiver/JustInTime.hpp"
|
||||||
|
|
||||||
//#define LOG_TRACE
|
//#define LOG_TRACE
|
||||||
|
|
||||||
#include "../../../Components/6522/6522.hpp"
|
#include "../../../Components/6522/6522.hpp"
|
||||||
@ -456,7 +458,7 @@ template <Analyser::Static::Macintosh::Target::Model model> class ConcreteMachin
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
void update_video() {
|
void update_video() {
|
||||||
video_.run_for(time_since_video_update_.flush());
|
video_.run_for(time_since_video_update_.flush<HalfCycles>());
|
||||||
time_until_video_event_ = video_.get_next_sequence_point();
|
time_until_video_event_ = video_.get_next_sequence_point();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -471,7 +473,7 @@ template <Analyser::Static::Macintosh::Target::Model model> class ConcreteMachin
|
|||||||
Apple::IWM iwm;
|
Apple::IWM iwm;
|
||||||
|
|
||||||
void flush() {
|
void flush() {
|
||||||
iwm.run_for(time_since_update.flush_cycles());
|
iwm.run_for(time_since_update.flush<Cycles>());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -610,7 +612,6 @@ template <Analyser::Static::Macintosh::Target::Model model> class ConcreteMachin
|
|||||||
HalfCycles keyboard_clock_;
|
HalfCycles keyboard_clock_;
|
||||||
HalfCycles time_since_video_update_;
|
HalfCycles time_since_video_update_;
|
||||||
HalfCycles time_until_video_event_;
|
HalfCycles time_until_video_event_;
|
||||||
HalfCycles time_since_iwm_update_;
|
|
||||||
HalfCycles time_since_mouse_update_;
|
HalfCycles time_since_mouse_update_;
|
||||||
|
|
||||||
bool ROM_is_overlay_ = true;
|
bool ROM_is_overlay_ = true;
|
||||||
|
@ -55,13 +55,13 @@ class Bus {
|
|||||||
// video backlog accumulation counter
|
// video backlog accumulation counter
|
||||||
Cycles cycles_since_video_update_;
|
Cycles cycles_since_video_update_;
|
||||||
inline void update_video() {
|
inline void update_video() {
|
||||||
tia_.run_for(cycles_since_video_update_.flush());
|
tia_.run_for(cycles_since_video_update_.flush<Cycles>());
|
||||||
}
|
}
|
||||||
|
|
||||||
// RIOT backlog accumulation counter
|
// RIOT backlog accumulation counter
|
||||||
Cycles cycles_since_6532_update_;
|
Cycles cycles_since_6532_update_;
|
||||||
inline void update_6532() {
|
inline void update_6532() {
|
||||||
mos6532_.run_for(cycles_since_6532_update_.flush());
|
mos6532_.run_for(cycles_since_6532_update_.flush<Cycles>());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
#include "../../Configurable/StandardOptions.hpp"
|
#include "../../Configurable/StandardOptions.hpp"
|
||||||
#include "../../ClockReceiver/ForceInline.hpp"
|
#include "../../ClockReceiver/ForceInline.hpp"
|
||||||
|
#include "../../ClockReceiver/JustInTime.hpp"
|
||||||
|
|
||||||
#include "../../Outputs/Speaker/Implementation/CompoundSource.hpp"
|
#include "../../Outputs/Speaker/Implementation/CompoundSource.hpp"
|
||||||
#include "../../Outputs/Speaker/Implementation/LowpassSpeaker.hpp"
|
#include "../../Outputs/Speaker/Implementation/LowpassSpeaker.hpp"
|
||||||
@ -169,7 +170,7 @@ class ConcreteMachine:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ColecoVisions have composite output only.
|
// ColecoVisions have composite output only.
|
||||||
vdp_.set_display_type(Outputs::Display::DisplayType::CompositeColour);
|
vdp_->set_display_type(Outputs::Display::DisplayType::CompositeColour);
|
||||||
}
|
}
|
||||||
|
|
||||||
~ConcreteMachine() {
|
~ConcreteMachine() {
|
||||||
@ -181,11 +182,11 @@ class ConcreteMachine:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void set_scan_target(Outputs::Display::ScanTarget *scan_target) override {
|
void set_scan_target(Outputs::Display::ScanTarget *scan_target) override {
|
||||||
vdp_.set_scan_target(scan_target);
|
vdp_->set_scan_target(scan_target);
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_display_type(Outputs::Display::DisplayType display_type) override {
|
void set_display_type(Outputs::Display::DisplayType display_type) override {
|
||||||
vdp_.set_display_type(display_type);
|
vdp_->set_display_type(display_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
Outputs::Speaker::Speaker *get_speaker() override {
|
Outputs::Speaker::Speaker *get_speaker() override {
|
||||||
@ -210,7 +211,7 @@ class ConcreteMachine:
|
|||||||
}
|
}
|
||||||
const HalfCycles length = cycle.length + penalty;
|
const HalfCycles length = cycle.length + penalty;
|
||||||
|
|
||||||
time_since_vdp_update_ += length;
|
vdp_ += length;
|
||||||
time_since_sn76489_update_ += length;
|
time_since_sn76489_update_ += length;
|
||||||
|
|
||||||
// Act only if necessary.
|
// Act only if necessary.
|
||||||
@ -255,10 +256,9 @@ class ConcreteMachine:
|
|||||||
case CPU::Z80::PartialMachineCycle::Input:
|
case CPU::Z80::PartialMachineCycle::Input:
|
||||||
switch((address >> 5) & 7) {
|
switch((address >> 5) & 7) {
|
||||||
case 5:
|
case 5:
|
||||||
update_video();
|
*cycle.value = vdp_->get_register(address);
|
||||||
*cycle.value = vdp_.get_register(address);
|
z80_.set_non_maskable_interrupt_line(vdp_->get_interrupt_line());
|
||||||
z80_.set_non_maskable_interrupt_line(vdp_.get_interrupt_line());
|
time_until_interrupt_ = vdp_->get_time_until_interrupt();
|
||||||
time_until_interrupt_ = vdp_.get_time_until_interrupt();
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 7: {
|
case 7: {
|
||||||
@ -299,10 +299,9 @@ class ConcreteMachine:
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 5:
|
case 5:
|
||||||
update_video();
|
vdp_->set_register(address, *cycle.value);
|
||||||
vdp_.set_register(address, *cycle.value);
|
z80_.set_non_maskable_interrupt_line(vdp_->get_interrupt_line());
|
||||||
z80_.set_non_maskable_interrupt_line(vdp_.get_interrupt_line());
|
time_until_interrupt_ = vdp_->get_time_until_interrupt();
|
||||||
time_until_interrupt_ = vdp_.get_time_until_interrupt();
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 7:
|
case 7:
|
||||||
@ -354,7 +353,7 @@ class ConcreteMachine:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void flush() {
|
void flush() {
|
||||||
update_video();
|
vdp_.flush();
|
||||||
update_audio();
|
update_audio();
|
||||||
audio_queue_.perform();
|
audio_queue_.perform();
|
||||||
}
|
}
|
||||||
@ -396,12 +395,9 @@ class ConcreteMachine:
|
|||||||
inline void update_audio() {
|
inline void update_audio() {
|
||||||
speaker_.run_for(audio_queue_, time_since_sn76489_update_.divide_cycles(Cycles(sn76489_divider)));
|
speaker_.run_for(audio_queue_, time_since_sn76489_update_.divide_cycles(Cycles(sn76489_divider)));
|
||||||
}
|
}
|
||||||
inline void update_video() {
|
|
||||||
vdp_.run_for(time_since_vdp_update_.flush());
|
|
||||||
}
|
|
||||||
|
|
||||||
CPU::Z80::Processor<ConcreteMachine, false, false> z80_;
|
CPU::Z80::Processor<ConcreteMachine, false, false> z80_;
|
||||||
TI::TMS::TMS9918 vdp_;
|
JustInTimeActor<TI::TMS::TMS9918, HalfCycles> vdp_;
|
||||||
|
|
||||||
Concurrency::DeferringAsyncTaskQueue audio_queue_;
|
Concurrency::DeferringAsyncTaskQueue audio_queue_;
|
||||||
TI::SN76489 sn76489_;
|
TI::SN76489 sn76489_;
|
||||||
@ -424,7 +420,6 @@ class ConcreteMachine:
|
|||||||
std::vector<std::unique_ptr<Inputs::Joystick>> joysticks_;
|
std::vector<std::unique_ptr<Inputs::Joystick>> joysticks_;
|
||||||
bool joysticks_in_keypad_mode_ = false;
|
bool joysticks_in_keypad_mode_ = false;
|
||||||
|
|
||||||
HalfCycles time_since_vdp_update_;
|
|
||||||
HalfCycles time_since_sn76489_update_;
|
HalfCycles time_since_sn76489_update_;
|
||||||
HalfCycles time_until_interrupt_;
|
HalfCycles time_until_interrupt_;
|
||||||
|
|
||||||
|
@ -702,7 +702,7 @@ class ConcreteMachine:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
void update_video() {
|
void update_video() {
|
||||||
mos6560_.run_for(cycles_since_mos6560_update_.flush());
|
mos6560_.run_for(cycles_since_mos6560_update_.flush<Cycles>());
|
||||||
}
|
}
|
||||||
CPU::MOS6502::Processor<CPU::MOS6502::Personality::P6502, ConcreteMachine, false> m6502_;
|
CPU::MOS6502::Processor<CPU::MOS6502::Personality::P6502, ConcreteMachine, false> m6502_;
|
||||||
|
|
||||||
|
@ -510,7 +510,7 @@ class ConcreteMachine:
|
|||||||
// MARK: - Work deferral updates.
|
// MARK: - Work deferral updates.
|
||||||
inline void update_display() {
|
inline void update_display() {
|
||||||
if(cycles_since_display_update_ > 0) {
|
if(cycles_since_display_update_ > 0) {
|
||||||
video_output_.run_for(cycles_since_display_update_.flush());
|
video_output_.run_for(cycles_since_display_update_.flush<Cycles>());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,6 +45,7 @@
|
|||||||
|
|
||||||
#include "../../Configurable/StandardOptions.hpp"
|
#include "../../Configurable/StandardOptions.hpp"
|
||||||
#include "../../ClockReceiver/ForceInline.hpp"
|
#include "../../ClockReceiver/ForceInline.hpp"
|
||||||
|
#include "../../ClockReceiver/JustInTime.hpp"
|
||||||
|
|
||||||
#include "../../Analyser/Static/MSX/Target.hpp"
|
#include "../../Analyser/Static/MSX/Target.hpp"
|
||||||
|
|
||||||
@ -187,7 +188,7 @@ class ConcreteMachine:
|
|||||||
switch(target.region) {
|
switch(target.region) {
|
||||||
case Target::Region::Japan:
|
case Target::Region::Japan:
|
||||||
required_roms.emplace_back(machine_name, "a Japanese MSX BIOS", "msx-japanese.rom", 32*1024, 0xee229390);
|
required_roms.emplace_back(machine_name, "a Japanese MSX BIOS", "msx-japanese.rom", 32*1024, 0xee229390);
|
||||||
vdp_.set_tv_standard(TI::TMS::TVStandard::NTSC);
|
vdp_->set_tv_standard(TI::TMS::TVStandard::NTSC);
|
||||||
|
|
||||||
is_ntsc = true;
|
is_ntsc = true;
|
||||||
character_generator = 0;
|
character_generator = 0;
|
||||||
@ -195,7 +196,7 @@ class ConcreteMachine:
|
|||||||
break;
|
break;
|
||||||
case Target::Region::USA:
|
case Target::Region::USA:
|
||||||
required_roms.emplace_back(machine_name, "an American MSX BIOS", "msx-american.rom", 32*1024, 0);
|
required_roms.emplace_back(machine_name, "an American MSX BIOS", "msx-american.rom", 32*1024, 0);
|
||||||
vdp_.set_tv_standard(TI::TMS::TVStandard::NTSC);
|
vdp_->set_tv_standard(TI::TMS::TVStandard::NTSC);
|
||||||
|
|
||||||
is_ntsc = true;
|
is_ntsc = true;
|
||||||
character_generator = 1;
|
character_generator = 1;
|
||||||
@ -203,7 +204,7 @@ class ConcreteMachine:
|
|||||||
break;
|
break;
|
||||||
case Target::Region::Europe:
|
case Target::Region::Europe:
|
||||||
required_roms.emplace_back(machine_name, "a European MSX BIOS", "msx-european.rom", 32*1024, 0);
|
required_roms.emplace_back(machine_name, "a European MSX BIOS", "msx-european.rom", 32*1024, 0);
|
||||||
vdp_.set_tv_standard(TI::TMS::TVStandard::PAL);
|
vdp_->set_tv_standard(TI::TMS::TVStandard::PAL);
|
||||||
|
|
||||||
is_ntsc = false;
|
is_ntsc = false;
|
||||||
character_generator = 1;
|
character_generator = 1;
|
||||||
@ -278,11 +279,11 @@ class ConcreteMachine:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void set_scan_target(Outputs::Display::ScanTarget *scan_target) override {
|
void set_scan_target(Outputs::Display::ScanTarget *scan_target) override {
|
||||||
vdp_.set_scan_target(scan_target);
|
vdp_->set_scan_target(scan_target);
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_display_type(Outputs::Display::DisplayType display_type) override {
|
void set_display_type(Outputs::Display::DisplayType display_type) override {
|
||||||
vdp_.set_display_type(display_type);
|
vdp_->set_display_type(display_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
Outputs::Speaker::Speaker *get_speaker() override {
|
Outputs::Speaker::Speaker *get_speaker() override {
|
||||||
@ -411,7 +412,7 @@ class ConcreteMachine:
|
|||||||
// but otherwise runs without pause.
|
// but otherwise runs without pause.
|
||||||
const HalfCycles addition((cycle.operation == CPU::Z80::PartialMachineCycle::ReadOpcode) ? 2 : 0);
|
const HalfCycles addition((cycle.operation == CPU::Z80::PartialMachineCycle::ReadOpcode) ? 2 : 0);
|
||||||
const HalfCycles total_length = addition + cycle.length;
|
const HalfCycles total_length = addition + cycle.length;
|
||||||
time_since_vdp_update_ += total_length;
|
vdp_ += total_length;
|
||||||
time_since_ay_update_ += total_length;
|
time_since_ay_update_ += total_length;
|
||||||
memory_slots_[0].cycles_since_update += total_length;
|
memory_slots_[0].cycles_since_update += total_length;
|
||||||
memory_slots_[1].cycles_since_update += total_length;
|
memory_slots_[1].cycles_since_update += total_length;
|
||||||
@ -489,7 +490,7 @@ class ConcreteMachine:
|
|||||||
*cycle.value = read_pointers_[address >> 13][address & 8191];
|
*cycle.value = read_pointers_[address >> 13][address & 8191];
|
||||||
} else {
|
} else {
|
||||||
int slot_hit = (paged_memory_ >> ((address >> 14) * 2)) & 3;
|
int slot_hit = (paged_memory_ >> ((address >> 14) * 2)) & 3;
|
||||||
memory_slots_[slot_hit].handler->run_for(memory_slots_[slot_hit].cycles_since_update.flush());
|
memory_slots_[slot_hit].handler->run_for(memory_slots_[slot_hit].cycles_since_update.flush<HalfCycles>());
|
||||||
*cycle.value = memory_slots_[slot_hit].handler->read(address);
|
*cycle.value = memory_slots_[slot_hit].handler->read(address);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -500,7 +501,7 @@ class ConcreteMachine:
|
|||||||
int slot_hit = (paged_memory_ >> ((address >> 14) * 2)) & 3;
|
int slot_hit = (paged_memory_ >> ((address >> 14) * 2)) & 3;
|
||||||
if(memory_slots_[slot_hit].handler) {
|
if(memory_slots_[slot_hit].handler) {
|
||||||
update_audio();
|
update_audio();
|
||||||
memory_slots_[slot_hit].handler->run_for(memory_slots_[slot_hit].cycles_since_update.flush());
|
memory_slots_[slot_hit].handler->run_for(memory_slots_[slot_hit].cycles_since_update.flush<HalfCycles>());
|
||||||
memory_slots_[slot_hit].handler->write(address, *cycle.value, read_pointers_[pc_address_ >> 13] != memory_slots_[0].read_pointers[pc_address_ >> 13]);
|
memory_slots_[slot_hit].handler->write(address, *cycle.value, read_pointers_[pc_address_ >> 13] != memory_slots_[0].read_pointers[pc_address_ >> 13]);
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
@ -508,10 +509,9 @@ class ConcreteMachine:
|
|||||||
case CPU::Z80::PartialMachineCycle::Input:
|
case CPU::Z80::PartialMachineCycle::Input:
|
||||||
switch(address & 0xff) {
|
switch(address & 0xff) {
|
||||||
case 0x98: case 0x99:
|
case 0x98: case 0x99:
|
||||||
vdp_.run_for(time_since_vdp_update_.flush());
|
*cycle.value = vdp_->get_register(address);
|
||||||
*cycle.value = vdp_.get_register(address);
|
z80_.set_interrupt_line(vdp_->get_interrupt_line());
|
||||||
z80_.set_interrupt_line(vdp_.get_interrupt_line());
|
time_until_interrupt_ = vdp_->get_time_until_interrupt();
|
||||||
time_until_interrupt_ = vdp_.get_time_until_interrupt();
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0xa2:
|
case 0xa2:
|
||||||
@ -536,10 +536,9 @@ class ConcreteMachine:
|
|||||||
const int port = address & 0xff;
|
const int port = address & 0xff;
|
||||||
switch(port) {
|
switch(port) {
|
||||||
case 0x98: case 0x99:
|
case 0x98: case 0x99:
|
||||||
vdp_.run_for(time_since_vdp_update_.flush());
|
vdp_->set_register(address, *cycle.value);
|
||||||
vdp_.set_register(address, *cycle.value);
|
z80_.set_interrupt_line(vdp_->get_interrupt_line());
|
||||||
z80_.set_interrupt_line(vdp_.get_interrupt_line());
|
time_until_interrupt_ = vdp_->get_time_until_interrupt();
|
||||||
time_until_interrupt_ = vdp_.get_time_until_interrupt();
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0xa0: case 0xa1:
|
case 0xa0: case 0xa1:
|
||||||
@ -612,7 +611,7 @@ class ConcreteMachine:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void flush() {
|
void flush() {
|
||||||
vdp_.run_for(time_since_vdp_update_.flush());
|
vdp_.flush();
|
||||||
update_audio();
|
update_audio();
|
||||||
audio_queue_.perform();
|
audio_queue_.perform();
|
||||||
}
|
}
|
||||||
@ -753,7 +752,7 @@ class ConcreteMachine:
|
|||||||
};
|
};
|
||||||
|
|
||||||
CPU::Z80::Processor<ConcreteMachine, false, false> z80_;
|
CPU::Z80::Processor<ConcreteMachine, false, false> z80_;
|
||||||
TI::TMS::TMS9918 vdp_;
|
JustInTimeActor<TI::TMS::TMS9918, HalfCycles> vdp_;
|
||||||
Intel::i8255::i8255<i8255PortHandler> i8255_;
|
Intel::i8255::i8255<i8255PortHandler> i8255_;
|
||||||
|
|
||||||
Concurrency::DeferringAsyncTaskQueue audio_queue_;
|
Concurrency::DeferringAsyncTaskQueue audio_queue_;
|
||||||
@ -797,7 +796,6 @@ class ConcreteMachine:
|
|||||||
uint8_t scratch_[8192];
|
uint8_t scratch_[8192];
|
||||||
uint8_t unpopulated_[8192];
|
uint8_t unpopulated_[8192];
|
||||||
|
|
||||||
HalfCycles time_since_vdp_update_;
|
|
||||||
HalfCycles time_since_ay_update_;
|
HalfCycles time_since_ay_update_;
|
||||||
HalfCycles time_until_interrupt_;
|
HalfCycles time_until_interrupt_;
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
#include "../KeyboardMachine.hpp"
|
#include "../KeyboardMachine.hpp"
|
||||||
|
|
||||||
#include "../../ClockReceiver/ForceInline.hpp"
|
#include "../../ClockReceiver/ForceInline.hpp"
|
||||||
|
#include "../../ClockReceiver/JustInTime.hpp"
|
||||||
|
|
||||||
#include "../../Outputs/Speaker/Implementation/LowpassSpeaker.hpp"
|
#include "../../Outputs/Speaker/Implementation/LowpassSpeaker.hpp"
|
||||||
#include "../../Outputs/Log.hpp"
|
#include "../../Outputs/Log.hpp"
|
||||||
@ -168,16 +169,16 @@ class ConcreteMachine:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void set_scan_target(Outputs::Display::ScanTarget *scan_target) override {
|
void set_scan_target(Outputs::Display::ScanTarget *scan_target) override {
|
||||||
vdp_.set_tv_standard(
|
vdp_->set_tv_standard(
|
||||||
(region_ == Target::Region::Europe) ?
|
(region_ == Target::Region::Europe) ?
|
||||||
TI::TMS::TVStandard::PAL : TI::TMS::TVStandard::NTSC);
|
TI::TMS::TVStandard::PAL : TI::TMS::TVStandard::NTSC);
|
||||||
time_until_debounce_ = vdp_.get_time_until_line(-1);
|
time_until_debounce_ = vdp_->get_time_until_line(-1);
|
||||||
|
|
||||||
vdp_.set_scan_target(scan_target);
|
vdp_->set_scan_target(scan_target);
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_display_type(Outputs::Display::DisplayType display_type) override {
|
void set_display_type(Outputs::Display::DisplayType display_type) override {
|
||||||
vdp_.set_display_type(display_type);
|
vdp_->set_display_type(display_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
Outputs::Speaker::Speaker *get_speaker() override {
|
Outputs::Speaker::Speaker *get_speaker() override {
|
||||||
@ -189,7 +190,7 @@ class ConcreteMachine:
|
|||||||
}
|
}
|
||||||
|
|
||||||
forceinline HalfCycles perform_machine_cycle(const CPU::Z80::PartialMachineCycle &cycle) {
|
forceinline HalfCycles perform_machine_cycle(const CPU::Z80::PartialMachineCycle &cycle) {
|
||||||
time_since_vdp_update_ += cycle.length;
|
vdp_ += cycle.length;
|
||||||
time_since_sn76489_update_ += cycle.length;
|
time_since_sn76489_update_ += cycle.length;
|
||||||
|
|
||||||
if(cycle.is_terminal()) {
|
if(cycle.is_terminal()) {
|
||||||
@ -233,17 +234,15 @@ class ConcreteMachine:
|
|||||||
*cycle.value = 0xff;
|
*cycle.value = 0xff;
|
||||||
break;
|
break;
|
||||||
case 0x40:
|
case 0x40:
|
||||||
update_video();
|
*cycle.value = vdp_->get_current_line();
|
||||||
*cycle.value = vdp_.get_current_line();
|
|
||||||
break;
|
break;
|
||||||
case 0x41:
|
case 0x41:
|
||||||
*cycle.value = vdp_.get_latched_horizontal_counter();
|
*cycle.value = vdp_.last_valid()->get_latched_horizontal_counter();
|
||||||
break;
|
break;
|
||||||
case 0x80: case 0x81:
|
case 0x80: case 0x81:
|
||||||
update_video();
|
*cycle.value = vdp_->get_register(address);
|
||||||
*cycle.value = vdp_.get_register(address);
|
z80_.set_interrupt_line(vdp_->get_interrupt_line());
|
||||||
z80_.set_interrupt_line(vdp_.get_interrupt_line());
|
time_until_interrupt_ = vdp_->get_time_until_interrupt();
|
||||||
time_until_interrupt_ = vdp_.get_time_until_interrupt();
|
|
||||||
break;
|
break;
|
||||||
case 0xc0: {
|
case 0xc0: {
|
||||||
Joystick *const joypad1 = static_cast<Joystick *>(joysticks_[0].get());
|
Joystick *const joypad1 = static_cast<Joystick *>(joysticks_[0].get());
|
||||||
@ -284,8 +283,7 @@ class ConcreteMachine:
|
|||||||
|
|
||||||
// Latch if either TH has newly gone to 1.
|
// Latch if either TH has newly gone to 1.
|
||||||
if((new_ths^previous_ths)&new_ths) {
|
if((new_ths^previous_ths)&new_ths) {
|
||||||
update_video();
|
vdp_->latch_horizontal_counter();
|
||||||
vdp_.latch_horizontal_counter();
|
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
case 0x40: case 0x41:
|
case 0x40: case 0x41:
|
||||||
@ -293,10 +291,9 @@ class ConcreteMachine:
|
|||||||
sn76489_.set_register(*cycle.value);
|
sn76489_.set_register(*cycle.value);
|
||||||
break;
|
break;
|
||||||
case 0x80: case 0x81:
|
case 0x80: case 0x81:
|
||||||
update_video();
|
vdp_->set_register(address, *cycle.value);
|
||||||
vdp_.set_register(address, *cycle.value);
|
z80_.set_interrupt_line(vdp_->get_interrupt_line());
|
||||||
z80_.set_interrupt_line(vdp_.get_interrupt_line());
|
time_until_interrupt_ = vdp_->get_time_until_interrupt();
|
||||||
time_until_interrupt_ = vdp_.get_time_until_interrupt();
|
|
||||||
break;
|
break;
|
||||||
case 0xc0:
|
case 0xc0:
|
||||||
LOG("TODO: [output] I/O port A/N; " << int(*cycle.value));
|
LOG("TODO: [output] I/O port A/N; " << int(*cycle.value));
|
||||||
@ -331,15 +328,14 @@ class ConcreteMachine:
|
|||||||
time_until_debounce_ -= cycle.length;
|
time_until_debounce_ -= cycle.length;
|
||||||
if(time_until_debounce_ <= HalfCycles(0)) {
|
if(time_until_debounce_ <= HalfCycles(0)) {
|
||||||
z80_.set_non_maskable_interrupt_line(pause_is_pressed_);
|
z80_.set_non_maskable_interrupt_line(pause_is_pressed_);
|
||||||
update_video();
|
time_until_debounce_ = vdp_->get_time_until_line(-1);
|
||||||
time_until_debounce_ = vdp_.get_time_until_line(-1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return HalfCycles(0);
|
return HalfCycles(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void flush() {
|
void flush() {
|
||||||
update_video();
|
vdp_.flush();
|
||||||
update_audio();
|
update_audio();
|
||||||
audio_queue_.perform();
|
audio_queue_.perform();
|
||||||
}
|
}
|
||||||
@ -412,16 +408,13 @@ class ConcreteMachine:
|
|||||||
inline void update_audio() {
|
inline void update_audio() {
|
||||||
speaker_.run_for(audio_queue_, time_since_sn76489_update_.divide_cycles(Cycles(sn76489_divider)));
|
speaker_.run_for(audio_queue_, time_since_sn76489_update_.divide_cycles(Cycles(sn76489_divider)));
|
||||||
}
|
}
|
||||||
inline void update_video() {
|
|
||||||
vdp_.run_for(time_since_vdp_update_.flush());
|
|
||||||
}
|
|
||||||
|
|
||||||
using Target = Analyser::Static::Sega::Target;
|
using Target = Analyser::Static::Sega::Target;
|
||||||
Target::Model model_;
|
Target::Model model_;
|
||||||
Target::Region region_;
|
Target::Region region_;
|
||||||
Target::PagingScheme paging_scheme_;
|
Target::PagingScheme paging_scheme_;
|
||||||
CPU::Z80::Processor<ConcreteMachine, false, false> z80_;
|
CPU::Z80::Processor<ConcreteMachine, false, false> z80_;
|
||||||
TI::TMS::TMS9918 vdp_;
|
JustInTimeActor<TI::TMS::TMS9918, HalfCycles> vdp_;
|
||||||
|
|
||||||
Concurrency::DeferringAsyncTaskQueue audio_queue_;
|
Concurrency::DeferringAsyncTaskQueue audio_queue_;
|
||||||
TI::SN76489 sn76489_;
|
TI::SN76489 sn76489_;
|
||||||
@ -431,7 +424,6 @@ class ConcreteMachine:
|
|||||||
Inputs::Keyboard keyboard_;
|
Inputs::Keyboard keyboard_;
|
||||||
bool reset_is_pressed_ = false, pause_is_pressed_ = false;
|
bool reset_is_pressed_ = false, pause_is_pressed_ = false;
|
||||||
|
|
||||||
HalfCycles time_since_vdp_update_;
|
|
||||||
HalfCycles time_since_sn76489_update_;
|
HalfCycles time_since_sn76489_update_;
|
||||||
HalfCycles time_until_interrupt_;
|
HalfCycles time_until_interrupt_;
|
||||||
HalfCycles time_until_debounce_;
|
HalfCycles time_until_debounce_;
|
||||||
|
@ -182,7 +182,7 @@ class VIAPortHandler: public MOS::MOS6522::IRQDelegatePortHandler {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
void update_ay() {
|
void update_ay() {
|
||||||
speaker_.run_for(audio_queue_, cycles_since_ay_update_.flush_cycles());
|
speaker_.run_for(audio_queue_, cycles_since_ay_update_.flush<Cycles>());
|
||||||
}
|
}
|
||||||
bool ay_bdir_ = false;
|
bool ay_bdir_ = false;
|
||||||
bool ay_bc1_ = false;
|
bool ay_bc1_ = false;
|
||||||
@ -585,7 +585,7 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
|
|||||||
uint8_t ram_[65536];
|
uint8_t ram_[65536];
|
||||||
Cycles cycles_since_video_update_;
|
Cycles cycles_since_video_update_;
|
||||||
inline void update_video() {
|
inline void update_video() {
|
||||||
video_output_.run_for(cycles_since_video_update_.flush());
|
video_output_.run_for(cycles_since_video_update_.flush<Cycles>());
|
||||||
}
|
}
|
||||||
|
|
||||||
// ROM bookkeeping
|
// ROM bookkeeping
|
||||||
@ -617,7 +617,7 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
|
|||||||
Apple::DiskII diskii_;
|
Apple::DiskII diskii_;
|
||||||
Cycles cycles_since_diskii_update_;
|
Cycles cycles_since_diskii_update_;
|
||||||
void flush_diskii() {
|
void flush_diskii() {
|
||||||
diskii_.run_for(cycles_since_diskii_update_.flush());
|
diskii_.run_for(cycles_since_diskii_update_.flush<Cycles>());
|
||||||
}
|
}
|
||||||
std::vector<uint8_t> pravetz_rom_;
|
std::vector<uint8_t> pravetz_rom_;
|
||||||
std::size_t pravetz_rom_base_pointer_ = 0;
|
std::size_t pravetz_rom_base_pointer_ = 0;
|
||||||
|
@ -983,6 +983,7 @@
|
|||||||
4B7F188D2154825D00388727 /* MasterSystem.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = MasterSystem.hpp; sourceTree = "<group>"; };
|
4B7F188D2154825D00388727 /* MasterSystem.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = MasterSystem.hpp; sourceTree = "<group>"; };
|
||||||
4B7F1895215486A100388727 /* StaticAnalyser.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = StaticAnalyser.hpp; sourceTree = "<group>"; };
|
4B7F1895215486A100388727 /* StaticAnalyser.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = StaticAnalyser.hpp; sourceTree = "<group>"; };
|
||||||
4B7F1896215486A100388727 /* StaticAnalyser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StaticAnalyser.cpp; sourceTree = "<group>"; };
|
4B7F1896215486A100388727 /* StaticAnalyser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StaticAnalyser.cpp; sourceTree = "<group>"; };
|
||||||
|
4B80214322EE7C3E00068002 /* JustInTime.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = JustInTime.hpp; sourceTree = "<group>"; };
|
||||||
4B80ACFE1F85CAC900176895 /* BestEffortUpdater.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = BestEffortUpdater.cpp; path = ../../Concurrency/BestEffortUpdater.cpp; sourceTree = "<group>"; };
|
4B80ACFE1F85CAC900176895 /* BestEffortUpdater.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = BestEffortUpdater.cpp; path = ../../Concurrency/BestEffortUpdater.cpp; sourceTree = "<group>"; };
|
||||||
4B80ACFF1F85CACA00176895 /* BestEffortUpdater.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = BestEffortUpdater.hpp; path = ../../Concurrency/BestEffortUpdater.hpp; sourceTree = "<group>"; };
|
4B80ACFF1F85CACA00176895 /* BestEffortUpdater.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = BestEffortUpdater.hpp; path = ../../Concurrency/BestEffortUpdater.hpp; sourceTree = "<group>"; };
|
||||||
4B8334811F5D9FF70097E338 /* PartialMachineCycle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PartialMachineCycle.cpp; sourceTree = "<group>"; };
|
4B8334811F5D9FF70097E338 /* PartialMachineCycle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PartialMachineCycle.cpp; sourceTree = "<group>"; };
|
||||||
@ -1051,7 +1052,7 @@
|
|||||||
4B894516201967B4007DE474 /* StaticAnalyser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StaticAnalyser.cpp; sourceTree = "<group>"; };
|
4B894516201967B4007DE474 /* StaticAnalyser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StaticAnalyser.cpp; sourceTree = "<group>"; };
|
||||||
4B894517201967B4007DE474 /* StaticAnalyser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StaticAnalyser.cpp; sourceTree = "<group>"; };
|
4B894517201967B4007DE474 /* StaticAnalyser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StaticAnalyser.cpp; sourceTree = "<group>"; };
|
||||||
4B894540201967D6007DE474 /* Machines.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Machines.hpp; sourceTree = "<group>"; };
|
4B894540201967D6007DE474 /* Machines.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Machines.hpp; sourceTree = "<group>"; };
|
||||||
4B8A7E85212F988200F2BBC6 /* ClockDeferrer.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ClockDeferrer.hpp; sourceTree = "<group>"; };
|
4B8A7E85212F988200F2BBC6 /* DeferredQueue.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = DeferredQueue.hpp; sourceTree = "<group>"; };
|
||||||
4B8D287E1F77207100645199 /* TrackSerialiser.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = TrackSerialiser.hpp; sourceTree = "<group>"; };
|
4B8D287E1F77207100645199 /* TrackSerialiser.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = TrackSerialiser.hpp; sourceTree = "<group>"; };
|
||||||
4B8E4ECD1DCE483D003716C3 /* KeyboardMachine.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = KeyboardMachine.hpp; sourceTree = "<group>"; };
|
4B8E4ECD1DCE483D003716C3 /* KeyboardMachine.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = KeyboardMachine.hpp; sourceTree = "<group>"; };
|
||||||
4B8EF6071FE5AF830076CCDD /* LowpassSpeaker.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = LowpassSpeaker.hpp; sourceTree = "<group>"; };
|
4B8EF6071FE5AF830076CCDD /* LowpassSpeaker.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = LowpassSpeaker.hpp; sourceTree = "<group>"; };
|
||||||
@ -3383,10 +3384,11 @@
|
|||||||
4BF660691F281573002CB053 /* ClockReceiver */ = {
|
4BF660691F281573002CB053 /* ClockReceiver */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
4B8A7E85212F988200F2BBC6 /* ClockDeferrer.hpp */,
|
|
||||||
4BB146C61F49D7D700253439 /* ClockingHintSource.hpp */,
|
4BB146C61F49D7D700253439 /* ClockingHintSource.hpp */,
|
||||||
4BF6606A1F281573002CB053 /* ClockReceiver.hpp */,
|
4BF6606A1F281573002CB053 /* ClockReceiver.hpp */,
|
||||||
|
4B8A7E85212F988200F2BBC6 /* DeferredQueue.hpp */,
|
||||||
4BB06B211F316A3F00600C7A /* ForceInline.hpp */,
|
4BB06B211F316A3F00600C7A /* ForceInline.hpp */,
|
||||||
|
4B80214322EE7C3E00068002 /* JustInTime.hpp */,
|
||||||
4B449C942063389900A095C8 /* TimeTypes.hpp */,
|
4B449C942063389900A095C8 /* TimeTypes.hpp */,
|
||||||
);
|
);
|
||||||
name = ClockReceiver;
|
name = ClockReceiver;
|
||||||
|
Loading…
Reference in New Issue
Block a user