2016-01-18 21:46:41 +00:00
|
|
|
//
|
|
|
|
// Tape.hpp
|
|
|
|
// Clock Signal
|
|
|
|
//
|
|
|
|
// Created by Thomas Harte on 18/01/2016.
|
2018-05-13 19:19:52 +00:00
|
|
|
// Copyright 2016 Thomas Harte. All rights reserved.
|
2016-01-18 21:46:41 +00:00
|
|
|
//
|
|
|
|
|
|
|
|
#ifndef Tape_hpp
|
|
|
|
#define Tape_hpp
|
|
|
|
|
2016-06-26 23:03:57 +00:00
|
|
|
#include <memory>
|
2017-07-26 00:20:55 +00:00
|
|
|
|
|
|
|
#include "../../ClockReceiver/ClockReceiver.hpp"
|
2018-05-28 03:17:06 +00:00
|
|
|
#include "../../ClockReceiver/ClockingHintSource.hpp"
|
2017-08-20 16:18:36 +00:00
|
|
|
|
2016-07-29 11:15:46 +00:00
|
|
|
#include "../TimedEventLoop.hpp"
|
2016-01-18 21:46:41 +00:00
|
|
|
|
|
|
|
namespace Storage {
|
2016-08-27 21:09:45 +00:00
|
|
|
namespace Tape {
|
2016-01-18 21:46:41 +00:00
|
|
|
|
2016-07-10 12:54:39 +00:00
|
|
|
/*!
|
|
|
|
Models a tape as a sequence of pulses, each pulse being of arbitrary length and described
|
|
|
|
by their relationship with zero:
|
|
|
|
- high pulses exit from zero upward before returning to it;
|
|
|
|
- low pulses exit from zero downward before returning to it;
|
|
|
|
- zero pulses run along zero.
|
2017-11-08 03:51:06 +00:00
|
|
|
|
2016-07-10 12:54:39 +00:00
|
|
|
Subclasses should implement at least @c get_next_pulse and @c reset to provide a serial feeding
|
|
|
|
of pulses and the ability to return to the start of the feed. They may also implement @c seek if
|
|
|
|
a better implementation than a linear search from the @c reset time can be implemented.
|
|
|
|
*/
|
2016-01-18 21:46:41 +00:00
|
|
|
class Tape {
|
|
|
|
public:
|
2016-01-18 23:06:09 +00:00
|
|
|
struct Pulse {
|
2016-09-08 11:41:44 +00:00
|
|
|
enum Type {
|
2016-01-18 21:46:41 +00:00
|
|
|
High, Low, Zero
|
|
|
|
} type;
|
|
|
|
Time length;
|
2016-09-08 11:41:44 +00:00
|
|
|
|
|
|
|
Pulse(Type type, Time length) : type(type), length(length) {}
|
|
|
|
Pulse() {}
|
2016-01-18 21:46:41 +00:00
|
|
|
};
|
|
|
|
|
2016-09-11 21:09:00 +00:00
|
|
|
/*!
|
|
|
|
If at the start of the tape returns the first stored pulse. Otherwise advances past
|
|
|
|
the last-returned pulse and returns the next.
|
2016-08-30 01:53:06 +00:00
|
|
|
|
2016-09-11 21:09:00 +00:00
|
|
|
@returns the pulse that begins at the current cursor position.
|
|
|
|
*/
|
|
|
|
Pulse get_next_pulse();
|
|
|
|
|
|
|
|
/// Returns the tape to the beginning.
|
|
|
|
void reset();
|
|
|
|
|
|
|
|
/// @returns @c true if the tape has progressed beyond all recorded content; @c false otherwise.
|
2016-08-30 01:53:06 +00:00
|
|
|
virtual bool is_at_end() = 0;
|
2016-01-18 21:46:41 +00:00
|
|
|
|
2017-07-21 22:55:03 +00:00
|
|
|
/*!
|
|
|
|
Returns a numerical representation of progression into the tape. Precision is arbitrary but
|
|
|
|
required to be at least to the whole pulse. Greater numbers are later than earlier numbers,
|
|
|
|
but not necessarily continuous.
|
|
|
|
*/
|
|
|
|
virtual uint64_t get_offset();
|
|
|
|
|
|
|
|
/*!
|
|
|
|
Moves the tape to the first time at which the specified offset would be returned by get_offset.
|
|
|
|
*/
|
|
|
|
virtual void set_offset(uint64_t);
|
2016-09-11 21:09:00 +00:00
|
|
|
|
2017-07-21 22:55:03 +00:00
|
|
|
/*!
|
|
|
|
Calculates and returns the amount of time that has elapsed since the time began. Potentially expensive.
|
|
|
|
*/
|
|
|
|
virtual Time get_current_time();
|
|
|
|
|
|
|
|
/*!
|
|
|
|
Seeks to @c time. Potentially expensive.
|
|
|
|
*/
|
2016-09-11 21:09:00 +00:00
|
|
|
virtual void seek(Time &time);
|
|
|
|
|
2017-07-12 02:41:10 +00:00
|
|
|
virtual ~Tape() {};
|
|
|
|
|
2016-09-11 21:09:00 +00:00
|
|
|
private:
|
2017-07-21 22:55:03 +00:00
|
|
|
uint64_t offset_;
|
|
|
|
Tape::Pulse pulse_;
|
2016-09-11 21:09:00 +00:00
|
|
|
|
|
|
|
virtual Pulse virtual_get_next_pulse() = 0;
|
|
|
|
virtual void virtual_reset() = 0;
|
2016-01-18 21:46:41 +00:00
|
|
|
};
|
|
|
|
|
2016-07-10 12:54:39 +00:00
|
|
|
/*!
|
|
|
|
Provides a helper for: (i) retaining a reference to a tape; and (ii) running the tape at a certain
|
|
|
|
input clock rate.
|
|
|
|
|
|
|
|
Will call @c process_input_pulse instantaneously upon reaching *the end* of a pulse. Therefore a subclass
|
|
|
|
can decode pulses into data within process_input_pulse, using the supplied pulse's @c length and @c type.
|
|
|
|
*/
|
2018-05-28 03:17:06 +00:00
|
|
|
class TapePlayer: public TimedEventLoop, public ClockingHint::Source {
|
2016-06-26 23:03:57 +00:00
|
|
|
public:
|
2019-07-10 20:05:59 +00:00
|
|
|
TapePlayer(int input_clock_rate);
|
2016-06-26 23:03:57 +00:00
|
|
|
|
2016-08-27 21:09:45 +00:00
|
|
|
void set_tape(std::shared_ptr<Storage::Tape::Tape> tape);
|
2016-06-26 23:03:57 +00:00
|
|
|
bool has_tape();
|
2016-09-13 02:22:23 +00:00
|
|
|
std::shared_ptr<Storage::Tape::Tape> get_tape();
|
2016-06-26 23:03:57 +00:00
|
|
|
|
2017-07-28 02:05:29 +00:00
|
|
|
void run_for(const Cycles cycles);
|
2017-07-26 00:01:30 +00:00
|
|
|
|
2016-06-26 23:03:57 +00:00
|
|
|
void run_for_input_pulse();
|
2016-01-18 21:46:41 +00:00
|
|
|
|
2020-05-10 01:22:51 +00:00
|
|
|
ClockingHint::Preference preferred_clocking() const override;
|
2017-08-20 16:18:36 +00:00
|
|
|
|
2016-06-26 23:03:57 +00:00
|
|
|
protected:
|
2018-05-28 03:17:06 +00:00
|
|
|
virtual void process_next_event() override;
|
2017-07-16 23:49:31 +00:00
|
|
|
virtual void process_input_pulse(const Tape::Pulse &pulse) = 0;
|
2016-06-26 23:03:57 +00:00
|
|
|
|
|
|
|
private:
|
|
|
|
inline void get_next_pulse();
|
|
|
|
|
2016-12-03 17:05:19 +00:00
|
|
|
std::shared_ptr<Storage::Tape::Tape> tape_;
|
|
|
|
Tape::Pulse current_pulse_;
|
2016-06-26 23:03:57 +00:00
|
|
|
};
|
|
|
|
|
2016-10-20 23:33:25 +00:00
|
|
|
/*!
|
|
|
|
A specific subclass of the tape player for machines that sample such as to report only either a
|
|
|
|
high or a low current input level.
|
|
|
|
|
|
|
|
Such machines can use @c get_input() to get the current level of the input.
|
|
|
|
|
|
|
|
They can also provide a delegate to be notified upon any change in the input level.
|
|
|
|
*/
|
2019-08-16 03:14:40 +00:00
|
|
|
class BinaryTapePlayer : public TapePlayer {
|
2016-10-16 01:21:18 +00:00
|
|
|
public:
|
2019-07-10 20:05:59 +00:00
|
|
|
BinaryTapePlayer(int input_clock_rate);
|
2016-10-20 23:33:25 +00:00
|
|
|
void set_motor_control(bool enabled);
|
2017-12-27 03:13:28 +00:00
|
|
|
bool get_motor_control() const;
|
|
|
|
|
2016-10-20 23:33:25 +00:00
|
|
|
void set_tape_output(bool set);
|
2017-12-27 03:13:28 +00:00
|
|
|
bool get_input() const;
|
2016-10-16 01:21:18 +00:00
|
|
|
|
2017-07-28 02:05:29 +00:00
|
|
|
void run_for(const Cycles cycles);
|
2016-10-16 01:32:59 +00:00
|
|
|
|
2016-10-16 01:21:18 +00:00
|
|
|
class Delegate {
|
|
|
|
public:
|
|
|
|
virtual void tape_did_change_input(BinaryTapePlayer *tape_player) = 0;
|
|
|
|
};
|
2016-10-20 23:33:25 +00:00
|
|
|
void set_delegate(Delegate *delegate);
|
2016-10-16 01:21:18 +00:00
|
|
|
|
2020-05-10 01:22:51 +00:00
|
|
|
ClockingHint::Preference preferred_clocking() const final;
|
2017-08-20 16:18:36 +00:00
|
|
|
|
2016-11-03 02:30:53 +00:00
|
|
|
protected:
|
2017-11-11 03:57:03 +00:00
|
|
|
Delegate *delegate_ = nullptr;
|
2020-01-24 03:57:51 +00:00
|
|
|
void process_input_pulse(const Storage::Tape::Tape::Pulse &pulse) final;
|
2017-11-11 03:57:03 +00:00
|
|
|
bool input_level_ = false;
|
|
|
|
bool motor_is_running_ = false;
|
2016-10-16 01:21:18 +00:00
|
|
|
};
|
|
|
|
|
2016-08-27 21:09:45 +00:00
|
|
|
}
|
2016-06-26 23:03:57 +00:00
|
|
|
}
|
2016-01-18 21:46:41 +00:00
|
|
|
|
|
|
|
#endif /* Tape_hpp */
|