mirror of
https://github.com/TomHarte/CLK.git
synced 2024-12-24 12:30:17 +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:
parent
847e49ccdf
commit
8361756dc4
@ -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 */
|
||||||
|
@ -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,
|
||||||
|
@ -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();
|
||||||
|
|
||||||
|
@ -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.
|
||||||
*/
|
*/
|
||||||
|
@ -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);
|
||||||
|
@ -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"
|
||||||
|
@ -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,
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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();
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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_;
|
||||||
|
@ -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();
|
||||||
|
@ -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.
|
||||||
|
|
||||||
|
@ -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.
|
||||||
|
|
||||||
|
@ -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.
|
||||||
|
@ -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 {
|
||||||
|
@ -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.
|
||||||
|
Loading…
Reference in New Issue
Block a user