1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-12-25 03:32:01 +00:00

Switched definitively to the works-for-now approach of requiring an explicit opt-in where somebody wants to clock a whole-cycle receiver from a half-cycle clock.

This commit is contained in:
Thomas Harte 2017-07-27 07:40:02 -04:00
parent 847e49ccdf
commit 8361756dc4
17 changed files with 29 additions and 47 deletions

View File

@ -9,6 +9,8 @@
#ifndef ClockReceiver_hpp #ifndef ClockReceiver_hpp
#define ClockReceiver_hpp #define ClockReceiver_hpp
#include <cstdio>
/*! /*!
Provides a class that wraps a plain int, providing most of the basic arithmetic and Provides a class that wraps a plain int, providing most of the basic arithmetic and
Boolean operators, but forcing callers and receivers to be explicit as to usage. Boolean operators, but forcing callers and receivers to be explicit as to usage.
@ -118,13 +120,7 @@ class HalfCycles: public WrappedInt<HalfCycles> {
inline HalfCycles(const HalfCycles &half_cycles) : WrappedInt<HalfCycles>(half_cycles.length_) {} inline HalfCycles(const HalfCycles &half_cycles) : WrappedInt<HalfCycles>(half_cycles.length_) {}
}; };
/*! /*
ClockReceiver is a template for components that receove a clock, measured either
in cycles or in half cycles. They are expected to implement either of the run_for
methods and to declare that they are `using` the other; buying into the template
means that the other run_for will automatically map appropriately to the implemented
one, so callers may use either.
Alignment rule: Alignment rule:
run_for(Cycles) may be called only at the start of a cycle. E.g. the following run_for(Cycles) may be called only at the start of a cycle. E.g. the following
@ -150,22 +146,24 @@ class HalfCycles: public WrappedInt<HalfCycles> {
of a full cycle. The second will do the second half. Etc. of a full cycle. The second will do the second half. Etc.
*/ */
template <class T> class ClockReceiver {
/*!
If a component implements only run_for(Cycles), an owner can wrap it in HalfClockReceiver
automatically to gain run_for(HalfCycles).
*/
template <class T> class HalfClockReceiver: public T {
public: public:
ClockReceiver() : half_cycle_carry_(0) {} using T::T;
inline void run_for(const Cycles &cycles) {
static_cast<T *>(this)->run_for(HalfCycles(cycles));
}
using T::run_for;
inline void run_for(const HalfCycles &half_cycles) { inline void run_for(const HalfCycles &half_cycles) {
int cycles = half_cycles.as_int() + half_cycle_carry_; int cycles = half_cycles.as_int() + half_cycle_carry_;
half_cycle_carry_ = cycles & 1; half_cycle_carry_ = cycles & 1;
static_cast<T *>(this)->run_for(Cycles(cycles >> 1)); T::run_for(Cycles(cycles >> 1));
} }
private: private:
int half_cycle_carry_; int half_cycle_carry_ = 0;
}; };
#endif /* ClockReceiver_hpp */ #endif /* ClockReceiver_hpp */

View File

@ -13,8 +13,6 @@
#include <typeinfo> #include <typeinfo>
#include <cstdio> #include <cstdio>
#include "../../ClockReceiver/ClockReceiver.hpp"
namespace MOS { namespace MOS {
/*! /*!
@ -28,7 +26,7 @@ namespace MOS {
Consumers should derive their own curiously-recurring-template-pattern subclass, Consumers should derive their own curiously-recurring-template-pattern subclass,
implementing bus communications as required. implementing bus communications as required.
*/ */
template <class T> class MOS6522: public ClockReceiver<MOS6522<T>> { template <class T> class MOS6522 {
private: private:
enum InterruptFlag: uint8_t { enum InterruptFlag: uint8_t {
CA2ActiveEdge = 1 << 0, CA2ActiveEdge = 1 << 0,

View File

@ -12,8 +12,6 @@
#include <cstdint> #include <cstdint>
#include <cstdio> #include <cstdio>
#include "../../ClockReceiver/ClockReceiver.hpp"
namespace MOS { namespace MOS {
/*! /*!
@ -27,7 +25,7 @@ namespace MOS {
Consumers should derive their own curiously-recurring-template-pattern subclass, Consumers should derive their own curiously-recurring-template-pattern subclass,
implementing bus communications as required. implementing bus communications as required.
*/ */
template <class T> class MOS6532: public ClockReceiver<MOS6532<T>> { template <class T> class MOS6532 {
public: public:
inline void set_ram(uint16_t address, uint8_t value) { ram_[address&0x7f] = value; } inline void set_ram(uint16_t address, uint8_t value) { ram_[address&0x7f] = value; }
inline uint8_t get_ram(uint16_t address) { return ram_[address & 0x7f]; } inline uint8_t get_ram(uint16_t address) { return ram_[address & 0x7f]; }
@ -106,7 +104,6 @@ template <class T> class MOS6532: public ClockReceiver<MOS6532<T>> {
return 0xff; return 0xff;
} }
using ClockReceiver<MOS6532<T>>::run_for;
inline void run_for(const Cycles &cycles) { inline void run_for(const Cycles &cycles) {
unsigned int number_of_cycles = (unsigned int)cycles.as_int(); unsigned int number_of_cycles = (unsigned int)cycles.as_int();

View File

@ -41,7 +41,7 @@ class Speaker: public ::Outputs::Filter<Speaker> {
@c set_register and @c get_register provide register access. @c set_register and @c get_register provide register access.
*/ */
template <class T> class MOS6560: public ClockReceiver<MOS6560<T>> { template <class T> class MOS6560 {
public: public:
MOS6560() : MOS6560() :
crt_(new Outputs::CRT::CRT(65*4, 4, Outputs::CRT::NTSC60, 2)), crt_(new Outputs::CRT::CRT(65*4, 4, Outputs::CRT::NTSC60, 2)),
@ -147,7 +147,6 @@ template <class T> class MOS6560: public ClockReceiver<MOS6560<T>> {
} }
} }
using ClockReceiver<MOS6560<T>>::run_for;
/*! /*!
Runs for cycles. Derr. Runs for cycles. Derr.
*/ */

View File

@ -12,11 +12,10 @@
#include <cstdint> #include <cstdint>
#include "../CRTMachine.hpp" #include "../CRTMachine.hpp"
#include "../../ClockReceiver/ClockReceiver.hpp"
namespace Atari2600 { namespace Atari2600 {
class TIA: public ClockReceiver<TIA> { class TIA {
public: public:
TIA(); TIA();
// The supplied hook is for unit testing only; if instantiated with a line_end_function then it will // The supplied hook is for unit testing only; if instantiated with a line_end_function then it will
@ -32,7 +31,6 @@ class TIA: public ClockReceiver<TIA> {
Advances the TIA by @c cycles. Any queued setters take effect in the first cycle performed. Advances the TIA by @c cycles. Any queued setters take effect in the first cycle performed.
*/ */
void run_for(const Cycles &cycles); void run_for(const Cycles &cycles);
using ClockReceiver<TIA>::run_for;
void set_output_mode(OutputMode output_mode); void set_output_mode(OutputMode output_mode);
void set_sync(bool sync); void set_sync(bool sync);

View File

@ -11,6 +11,7 @@
#include "../../Processors/6502/6502.hpp" #include "../../Processors/6502/6502.hpp"
#include "../../Storage/Tape/Tape.hpp" #include "../../Storage/Tape/Tape.hpp"
#include "../../ClockReceiver/ClockReceiver.hpp"
#include "../ConfigurationTarget.hpp" #include "../ConfigurationTarget.hpp"
#include "../CRTMachine.hpp" #include "../CRTMachine.hpp"

View File

@ -22,7 +22,7 @@ namespace Electron {
running either at 40 or 80 columns. Memory is shared between video and CPU; when the video running either at 40 or 80 columns. Memory is shared between video and CPU; when the video
is accessing it the CPU may not. is accessing it the CPU may not.
*/ */
class VideoOutput: public ClockReceiver<VideoOutput> { class VideoOutput {
public: public:
/*! /*!
Instantiates a VideoOutput that will read its pixels from @c memory. The pointer supplied Instantiates a VideoOutput that will read its pixels from @c memory. The pointer supplied
@ -35,7 +35,6 @@ class VideoOutput: public ClockReceiver<VideoOutput> {
/// Produces the next @c cycles of video output. /// Produces the next @c cycles of video output.
void run_for(const Cycles &cycles); void run_for(const Cycles &cycles);
using ClockReceiver<VideoOutput>::run_for;
/*! /*!
Writes @c value to the register at @c address. May mutate the results of @c get_next_interrupt, Writes @c value to the register at @c address. May mutate the results of @c get_next_interrupt,

View File

@ -14,12 +14,11 @@
namespace Oric { namespace Oric {
class VideoOutput: public ClockReceiver<VideoOutput> { class VideoOutput {
public: public:
VideoOutput(uint8_t *memory); VideoOutput(uint8_t *memory);
std::shared_ptr<Outputs::CRT::CRT> get_crt(); std::shared_ptr<Outputs::CRT::CRT> get_crt();
void run_for(const Cycles &cycles); void run_for(const Cycles &cycles);
using ClockReceiver<VideoOutput>::run_for;
void set_colour_rom(const std::vector<uint8_t> &rom); void set_colour_rom(const std::vector<uint8_t> &rom);
void set_output_device(Outputs::CRT::OutputDevice output_device); void set_output_device(Outputs::CRT::OutputDevice output_device);

View File

@ -24,7 +24,7 @@ namespace ZX8081 {
a 1-bit graphic and output over the next 4 cycles, picking between the white level a 1-bit graphic and output over the next 4 cycles, picking between the white level
and the black level. and the black level.
*/ */
class Video: public ClockReceiver<Video> { class Video {
public: public:
/// Constructs an instance of the video feed; a CRT is also created. /// Constructs an instance of the video feed; a CRT is also created.
Video(); Video();
@ -33,7 +33,6 @@ class Video: public ClockReceiver<Video> {
/// Advances time by @c cycles. /// Advances time by @c cycles.
void run_for(const HalfCycles &); void run_for(const HalfCycles &);
using ClockReceiver<Video>::run_for;
/// Forces output to catch up to the current output position. /// Forces output to catch up to the current output position.
void flush(); void flush();

View File

@ -180,7 +180,8 @@ HalfCycles Machine::perform_machine_cycle(const CPU::Z80::PartialMachineCycle &c
default: break; default: break;
} }
if(typer_) typer_->update(cycle.length.as_int()); // This will lose some precision; TODO: bring inside the [Half]ClockReceiver domain.
if(typer_) typer_->update(cycle.length.as_int() / 2);
return HalfCycles(0); return HalfCycles(0);
} }

View File

@ -100,7 +100,7 @@ class Machine:
void set_hsync(bool sync); void set_hsync(bool sync);
void update_sync(); void update_sync();
Storage::Tape::BinaryTapePlayer tape_player_; HalfClockReceiver<Storage::Tape::BinaryTapePlayer> tape_player_;
Storage::Tape::ZX8081::Parser parser_; Storage::Tape::ZX8081::Parser parser_;
bool is_zx81_; bool is_zx81_;

View File

@ -135,7 +135,7 @@ class Speaker {
Call `run_for` to request that the next period of input data is collected. Call `run_for` to request that the next period of input data is collected.
*/ */
template <class T> class Filter: public Speaker, public ClockReceiver<Filter<T>> { template <class T> class Filter: public Speaker {
public: public:
~Filter() { ~Filter() {
_queue->flush(); _queue->flush();

View File

@ -128,7 +128,7 @@ class ProcessorBase {
that will cause call outs when the program counter reaches those addresses. @c return_from_subroutine can be used to exit from a that will cause call outs when the program counter reaches those addresses. @c return_from_subroutine can be used to exit from a
jammed state. jammed state.
*/ */
template <class T> class Processor: public ProcessorBase, public ClockReceiver<Processor<T>> { template <class T> class Processor: public ProcessorBase {
private: private:
const MicroOp *scheduled_program_counter_; const MicroOp *scheduled_program_counter_;
@ -283,7 +283,6 @@ template <class T> class Processor: public ProcessorBase, public ClockReceiver<P
} }
public: public:
using ClockReceiver<Processor<T>>::run_for;
/*! /*!
Runs the 6502 for a supplied number of cycles. Runs the 6502 for a supplied number of cycles.

View File

@ -164,7 +164,7 @@ struct PartialMachineCycle {
order to provide the bus on which the Z80 operates and @c flush(), which is called upon completion of a continuous run order to provide the bus on which the Z80 operates and @c flush(), which is called upon completion of a continuous run
of cycles to allow a subclass to bring any on-demand activities up to date. of cycles to allow a subclass to bring any on-demand activities up to date.
*/ */
template <class T> class Processor: public ClockReceiver<Processor<T>> { template <class T> class Processor {
private: private:
uint8_t a_; uint8_t a_;
RegisterPair bc_, de_, hl_; RegisterPair bc_, de_, hl_;
@ -849,7 +849,6 @@ template <class T> class Processor: public ClockReceiver<Processor<T>> {
copy_program(irq_mode2_program, irq_program_[2]); copy_program(irq_mode2_program, irq_program_[2]);
} }
using ClockReceiver<Processor<T>>::run_for;
/*! /*!
Runs the Z80 for a supplied number of cycles. Runs the Z80 for a supplied number of cycles.

View File

@ -16,7 +16,7 @@
namespace Storage { namespace Storage {
class DigitalPhaseLockedLoop: public ClockReceiver<DigitalPhaseLockedLoop> { class DigitalPhaseLockedLoop {
public: public:
/*! /*!
Instantiates a @c DigitalPhaseLockedLoop. Instantiates a @c DigitalPhaseLockedLoop.
@ -32,7 +32,6 @@ class DigitalPhaseLockedLoop: public ClockReceiver<DigitalPhaseLockedLoop> {
@c number_of_cycles The time to run the loop for. @c number_of_cycles The time to run the loop for.
*/ */
void run_for(const Cycles &cycles); void run_for(const Cycles &cycles);
using ClockReceiver<DigitalPhaseLockedLoop>::run_for;
/*! /*!
Announces a pulse at the current time. Announces a pulse at the current time.

View File

@ -101,7 +101,6 @@ class TapePlayer: public TimedEventLoop {
bool has_tape(); bool has_tape();
std::shared_ptr<Storage::Tape::Tape> get_tape(); std::shared_ptr<Storage::Tape::Tape> get_tape();
using TimedEventLoop::run_for;
void run_for(const Cycles &cycles); void run_for(const Cycles &cycles);
void run_for_input_pulse(); void run_for_input_pulse();
@ -132,7 +131,6 @@ class BinaryTapePlayer: public TapePlayer {
void set_tape_output(bool set); void set_tape_output(bool set);
bool get_input(); bool get_input();
using TapePlayer::run_for;
void run_for(const Cycles &cycles); void run_for(const Cycles &cycles);
class Delegate { class Delegate {

View File

@ -10,7 +10,6 @@
#define TimedEventLoop_hpp #define TimedEventLoop_hpp
#include "Storage.hpp" #include "Storage.hpp"
#include "../ClockReceiver/ClockReceiver.hpp" #include "../ClockReceiver/ClockReceiver.hpp"
#include "../SignalProcessing/Stepper.hpp" #include "../SignalProcessing/Stepper.hpp"
@ -38,7 +37,7 @@ namespace Storage {
@c reset_timer to initiate a distinctly-timed stream or @c jump_to_next_event to short-circuit the timing @c reset_timer to initiate a distinctly-timed stream or @c jump_to_next_event to short-circuit the timing
loop and fast forward immediately to the next event. loop and fast forward immediately to the next event.
*/ */
class TimedEventLoop: public ClockReceiver<TimedEventLoop> { class TimedEventLoop {
public: public:
/*! /*!
Constructs a timed event loop that will be clocked at @c input_clock_rate. Constructs a timed event loop that will be clocked at @c input_clock_rate.
@ -49,7 +48,6 @@ namespace Storage {
Advances the event loop by @c number_of_cycles cycles. Advances the event loop by @c number_of_cycles cycles.
*/ */
void run_for(const Cycles &cycles); void run_for(const Cycles &cycles);
using ClockReceiver<TimedEventLoop>::run_for;
/*! /*!
@returns the number of whole cycles remaining until the next event is triggered. @returns the number of whole cycles remaining until the next event is triggered.