summaryrefslogtreecommitdiffstatshomepage
path: root/src/mame/machine/p2000t_mdcr.h
blob: 9b312ed42d7650e83b9292c05ce7b43507138c44 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
// 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_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override;

	virtual void device_add_mconfig(machine_config &config) override;

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<cassette_image_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