1 // license:BSD-3-Clause
2 // copyright-holders:AJR
3 /**********************************************************************
4 
5     National Semiconductor ADC0804 8-bit µP Compatible A/D Converter
6 
7     The ADC0804 is the most widely used member of a family of five
8     CMOS 8-bit ADCs with microprocessor-compatible interfaces, which
9     differ from each other only in error specifications:
10 
11       ADC0801       ±¼ LSB      Full-scale adjusted
12       ADC0802       ±½ LSB      Vref/2 = 2.500 Vdc
13       ADC0803       ±½ LSB      Full-scale adjusted
14       ADC0804       ±1 LSB      Vref/2 = 2.500 Vdc
15       ADC0805       ±1 LSB      Vref/2 = NC
16 
17     Though these devices are designed to convert differential analog
18     inputs, single-phase conversion can be achieved by tying pin 7
19     (negative Vin) to pin 8 (analog GND). A CD4051B or other analog
20     switch IC is often used to multiplex several input sources.
21 
22     Pin 4 should be connected to either an externally generated clock
23     signal (640 kHz typical, 1460 kHz maximum) or a timing capacitor
24     plus a resistor bridged to pin 19. The resistor is typically 10 kΩ
25     but higher values may be needed due to loading conditions when
26     multiple other devices are also clocked through pin 19, which
27     yields the output of the internal Schmitt trigger clock circuit.
28 
29     The ADC0804's INTR semaphore output reflects not the actual busy
30     state of the converter (as EOC does on the ADC0808) but an
31     internal flip-flop attached to it. This flip-flop is reset by
32     pulling either RD or WR active low; therefore, INTR will never
33     toggle if RD is tied to GND to keep the data outputs continuously
34     active.
35 
36 **********************************************************************/
37 
38 #include "emu.h"
39 #include "adc0804.h"
40 
41 
42 //**************************************************************************
43 //  TYPE DEFINITIONS
44 //**************************************************************************
45 
46 DEFINE_DEVICE_TYPE(ADC0803, adc0803_device, "adc0803", "ADC0803 A/D Converter")
47 DEFINE_DEVICE_TYPE(ADC0804, adc0804_device, "adc0804", "ADC0804 A/D Converter")
48 
49 ALLOW_SAVE_TYPE(adc0804_device::read_mode);
50 
51 
52 //**************************************************************************
53 //  DEVICE CONSTRUCTION AND INITIALIZATION
54 //**************************************************************************
55 
56 //-------------------------------------------------
57 //  adc0804_device - constructor
58 //-------------------------------------------------
59 
adc0804_device(const machine_config & mconfig,device_type type,const char * tag,device_t * owner,u32 clock)60 adc0804_device::adc0804_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock)
61 	: device_t(mconfig, type, tag, owner, clock)
62 	, m_vin_callback(*this)
63 	, m_intr_callback(*this)
64 	, m_res(0.0)
65 	, m_cap(0.0)
66 	, m_fclk_rc(attotime::zero)
67 	, m_timer(nullptr)
68 	, m_rd_mode(RD_STROBED)
69 	, m_rd_active(false)
70 	, m_wr_active(false)
71 	, m_result(0)
72 	, m_intr_active(false)
73 {
74 }
75 
adc0804_device(const machine_config & mconfig,const char * tag,device_t * owner,u32 clock)76 adc0804_device::adc0804_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
77 	: adc0804_device(mconfig, ADC0804, tag, owner, clock)
78 {
79 }
80 
81 
82 //-------------------------------------------------
83 //  adc0803_device - constructor
84 //-------------------------------------------------
85 
adc0803_device(const machine_config & mconfig,const char * tag,device_t * owner,u32 clock)86 adc0803_device::adc0803_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
87 	: adc0804_device(mconfig, ADC0803, tag, owner, clock)
88 {
89 }
90 
91 
92 //-------------------------------------------------
93 //  device_resolve_objects - resolve objects that
94 //  may be needed for other devices to set
95 //  initial conditions at start time
96 //-------------------------------------------------
97 
device_resolve_objects()98 void adc0804_device::device_resolve_objects()
99 {
100 	m_vin_callback.resolve_safe(0);
101 	m_intr_callback.resolve_safe();
102 
103 	if (m_rd_mode == RD_GROUNDED)
104 		m_rd_active = true;
105 }
106 
107 
108 //-------------------------------------------------
109 //  device_start - device-specific startup
110 //-------------------------------------------------
111 
device_start()112 void adc0804_device::device_start()
113 {
114 	// calculate RC timing
115 	if (m_res == 0.0 || m_cap == 0.0)
116 		m_fclk_rc = attotime::zero;
117 	else
118 		m_fclk_rc = attotime::from_double(m_res * m_cap / 1.1);
119 
120 	// create timer
121 	m_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(adc0804_device::conversion_done), this));
122 
123 	// save state
124 	if (m_rd_mode == RD_BITBANGED)
125 		save_item(NAME(m_rd_active));
126 	save_item(NAME(m_wr_active));
127 	save_item(NAME(m_result));
128 	save_item(NAME(m_intr_active));
129 }
130 
131 
132 //**************************************************************************
133 //  CONVERSION SEQUENCE
134 //**************************************************************************
135 
136 // Conversion occurs over a busy period of 66–73 cycles, after 1–8 cycles of latency
137 const int adc0804_device::s_conversion_cycles = 74;
138 
139 
140 //-------------------------------------------------
141 //  set_interrupt - set or clear INTR output
142 //-------------------------------------------------
143 
set_interrupt(bool state)144 void adc0804_device::set_interrupt(bool state)
145 {
146 	if (m_intr_active != state)
147 	{
148 		m_intr_active = state;
149 		m_intr_callback(state ? ASSERT_LINE : CLEAR_LINE);
150 	}
151 }
152 
153 
154 //-------------------------------------------------
155 //  conversion_start - begin the busy period
156 //-------------------------------------------------
157 
conversion_start()158 void adc0804_device::conversion_start()
159 {
160 	if (!m_timer->enabled())
161 		m_timer->adjust(m_fclk_rc != attotime::zero ? m_fclk_rc * s_conversion_cycles : clocks_to_attotime(s_conversion_cycles));
162 	else
163 		logerror("%s: Tried to start conversion when already in progress\n", machine().describe_context());
164 }
165 
166 
167 //-------------------------------------------------
168 //  conversion_done - load result and signal
169 //  interrupt
170 //-------------------------------------------------
171 
TIMER_CALLBACK_MEMBER(adc0804_device::conversion_done)172 TIMER_CALLBACK_MEMBER(adc0804_device::conversion_done)
173 {
174 	m_result = m_vin_callback();
175 
176 	if (!m_rd_active && !m_wr_active)
177 		set_interrupt(true);
178 }
179 
180 
181 //**************************************************************************
182 //  BUS INTERFACE
183 //**************************************************************************
184 
185 //-------------------------------------------------
186 //  read - read the 8-bit conversion result
187 //-------------------------------------------------
188 
read()189 u8 adc0804_device::read()
190 {
191 	switch (m_rd_mode)
192 	{
193 	case RD_STROBED:
194 		if (!machine().side_effects_disabled())
195 			set_interrupt(false);
196 		break;
197 
198 	case RD_BITBANGED:
199 		if (!m_rd_active)
200 			return 0xff; // open bus
201 		break;
202 
203 	case RD_GROUNDED:
204 		break;
205 	}
206 
207 	return m_result;
208 }
209 
210 
211 //-------------------------------------------------
212 //  write - begin a new conversion
213 //-------------------------------------------------
214 
write(u8 data)215 void adc0804_device::write(u8 data)
216 {
217 	// data is ignored
218 	set_interrupt(false);
219 	conversion_start();
220 }
221 
222 
223 //-------------------------------------------------
224 //  read_and_write - read the result of the last
225 //  conversion and begin a new one
226 //-------------------------------------------------
227 
read_and_write()228 u8 adc0804_device::read_and_write()
229 {
230 	if (!machine().side_effects_disabled())
231 	{
232 		set_interrupt(false);
233 		conversion_start();
234 	}
235 
236 	if (m_rd_mode == RD_BITBANGED && !m_rd_active)
237 		return 0xff; // open bus
238 	else
239 		return m_result;
240 }
241 
242 
243 //-------------------------------------------------
244 //  rd_w - enable data bus by line write
245 //-------------------------------------------------
246 
WRITE_LINE_MEMBER(adc0804_device::rd_w)247 WRITE_LINE_MEMBER(adc0804_device::rd_w)
248 {
249 	assert(m_rd_mode == RD_BITBANGED);
250 
251 	// RD input is active low
252 	if (!state && !m_rd_active)
253 	{
254 		m_rd_active = true;
255 		set_interrupt(false);
256 	}
257 	else if (state && m_rd_active)
258 		m_rd_active = false;
259 }
260 
261 
262 //-------------------------------------------------
263 //  wr_w - begin conversion by line write
264 //-------------------------------------------------
265 
WRITE_LINE_MEMBER(adc0804_device::wr_w)266 WRITE_LINE_MEMBER(adc0804_device::wr_w)
267 {
268 	// WR input is active low
269 	if (!state && !m_wr_active)
270 	{
271 		m_wr_active = true;
272 		set_interrupt(false);
273 	}
274 	else if (state && m_wr_active)
275 	{
276 		m_wr_active = false;
277 		conversion_start();
278 	}
279 }
280