// license:BSD-3-Clause // copyright-holders:Erwin Jansen /********************************************************************** Philips P2000t Mini Digital Cassette Recorder Emulation ********************************************************************** +12V 1 8 !WCD OV (signal) 2 9 !REV OV (power) 3 A !FWD GND 4 B RDC !WDA 6 C !RDA !BET 7 D !CIP E !WEN **********************************************************************/ #ifndef MAME_MACHINE_P2000T_MDCR_H #define MAME_MACHINE_P2000T_MDCR_H #pragma once #include "imagedev/cassette.h" /// \brief Models a MCR220 Micro Cassette Recorder /// /// Detailed documentation on the device can be found in this repository: /// https://github.com/p2000t/documentation/tree/master/hardware /// /// This device was built in the P2000t and was used as tape storage. /// It used mini-cassettes that were also used in dictation devices. /// The P2000t completely controls this device without user intervention. class mdcr_device : public device_t { public: mdcr_device(machine_config const &mconfig, char const *tag, device_t *owner, uint32_t clock); /// \brief The read clock, switches state when a bit is available. /// /// This is the read clock. The read clock is a flip flop that switches /// whenever a new bit is available on rda. The original flips every 167us /// This system flips at 154 / 176 usec, which is within the tolerance for /// the rom and system diagnostics. /// /// Note that rdc & rda are flipped when the tape is moving in reverse. DECLARE_READ_LINE_MEMBER(rdc); /// The current active data bit. DECLARE_READ_LINE_MEMBER(rda); /// False indicates we have reached end/beginning of tape DECLARE_READ_LINE_MEMBER(bet); /// False if a cassette is in place. DECLARE_READ_LINE_MEMBER(cip); /// False when the cassette is write enabled. DECLARE_READ_LINE_MEMBER(wen); /// True if we should activate the reverse motor. DECLARE_WRITE_LINE_MEMBER(rev); /// True if we should activate the forward motor. /// Note: A quick pulse (<20usec) will reset the phase decoder. DECLARE_WRITE_LINE_MEMBER(fwd); /// The bit to write to tape. Make sure to set wda after wdc. DECLARE_WRITE_LINE_MEMBER(wda); /// True if the current wda should be written to tape. DECLARE_WRITE_LINE_MEMBER(wdc); protected: virtual void device_start() override; virtual void device_pre_save() override; virtual void device_post_load() override; virtual void device_add_mconfig(machine_config &config) override; TIMER_CALLBACK_MEMBER(read_timer_tick); private: /// \brief A Phase Decoder used in a Philips MDCR220 Mini Cassette Recorder /// /// A phase decoder is capable of converting a signal stream into a /// a series of bits that go together with a clock signal. This phase /// decoder is conform to what you would find in an Philips MDCR220 /// /// Signals are converted into bits whenever the line signal /// changes from low to high and vice versa on a clock signal. /// /// A transition on a clock boundary from low to high is a 1. /// A transition on a clock boundary from high to low is a 0 /// An intermediate transition halfway between the clock boundary /// can occur when there are consecutive 0s or 1s. See the example /// below where the clock is marked by a | /// /// 1 0 1 1 0 0 /// RDA: _|----|____|--__|----|__--|__-- /// RDC: _|-___|-___|-___|-___|-___|-___ /// ^ ^ /// |-- clock signal |-- intermediate transition. /// /// This particular phase decoder expects a signal of /// 1010 1010 which is used to derive the clock T. /// after a reset. class phase_decoder { using time_in_seconds = double; public: /// Creates a phase decoder with the given tolerance. phase_decoder(double tolerance = 0.15); /// Pulls the bit out of the queue. bool pull_bit(); /// Returns true if a new bit can be read, you can now pull the bit. bool signal(bool state, double delay); /// Reset the clock state, the system will now need to resynchronize on 0xAA. void reset(); private: // add a bit and reset the current clock. void add_bit(bool bit); // tries to sync up the signal and calculate the clockperiod. bool sync_signal(bool state); // y * (1 - tolerance) < x < y * (1 + tolerance) bool within_tolerance(double x, double y); double m_tolerance; static constexpr int SYNCBITS = 7; static constexpr int QUEUE_DELAY = 2; public: // Needed for save state. bool m_last_signal{ false }; int m_needs_sync{ SYNCBITS }; uint8_t m_bit_queue{ 0 }; uint8_t m_bit_place{ 0 }; time_in_seconds m_current_clock{ 0 }; time_in_seconds m_clock_period{ 0 }; }; void write_bit(bool bit); void rewind(); void forward(); void stop(); bool tape_start_or_end(); bool m_fwd{ false }; bool m_rev{ false }; bool m_rdc{ false }; bool m_rda{ false }; bool m_wda{ false }; bool m_recording{ false }; double m_fwd_pulse_time{ 0 }; double m_last_tape_time{ 0 }; double m_save_tape_time{ 0 }; required_device m_cassette; phase_decoder m_phase_decoder; // timers emu_timer *m_read_timer; }; DECLARE_DEVICE_TYPE(MDCR, mdcr_device) #endif // MAME_MACHINE_P2000T_MDCR_H