1 // license:BSD-3-Clause
2 // copyright-holders:Curt Coder
3 /**********************************************************************
4 
5     RCA CDP1864C COS/MOS PAL Compatible Color TV Interface
6 
7 **********************************************************************/
8 
9 /*
10 
11     TODO:
12 
13     - interlace mode
14     - PAL output, currently using RGB
15     - cpu synchronization
16 
17         SC1 and SC0 are used to provide CDP1864C-to-CPU synchronization for a jitter-free display.
18         During every horizontal sync the CDP1864C samples SC0 and SC1 for SC0 = 1 and SC1 = 0
19         (CDP1800 execute state). Detection of a fetch cycle causes the CDP1864C to skip cycles to
20         attain synchronization. (i.e. picture moves 8 pixels to the right)
21 
22 */
23 
24 #include "emu.h"
25 #include "cdp1864.h"
26 #include "screen.h"
27 
28 
29 
30 //**************************************************************************
31 //  MACROS / CONSTANTS
32 //**************************************************************************
33 
34 #define CDP1864_DEFAULT_LATCH       0x35
35 
36 #define CDP1864_CYCLES_DMA_START    2*8
37 #define CDP1864_CYCLES_DMA_ACTIVE   8*8
38 #define CDP1864_CYCLES_DMA_WAIT     6*8
39 
40 constexpr int cdp1864_device::bckgnd[4];
41 
42 
43 
44 //**************************************************************************
45 //  DEVICE DEFINITIONS
46 //**************************************************************************
47 
48 // devices
49 DEFINE_DEVICE_TYPE(CDP1864, cdp1864_device, "cdp1864", "RCA CDP1864")
50 
51 
52 
53 //**************************************************************************
54 //  LIVE DEVICE
55 //**************************************************************************
56 
57 //-------------------------------------------------
58 //  cdp1864_device - constructor
59 //-------------------------------------------------
60 
cdp1864_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)61 cdp1864_device::cdp1864_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
62 	: device_t(mconfig, CDP1864, tag, owner, clock),
63 		device_sound_interface(mconfig, *this),
64 		device_video_interface(mconfig, *this),
65 		m_read_inlace(*this),
66 		m_read_rdata(*this),
67 		m_read_bdata(*this),
68 		m_read_gdata(*this),
69 		m_write_int(*this),
70 		m_write_dma_out(*this),
71 		m_write_efx(*this),
72 		m_write_hsync(*this),
73 		m_disp(0),
74 		m_dmaout(0),
75 		m_bgcolor(0),
76 		m_con(0),
77 		m_aoe(0),
78 		m_latch(CDP1864_DEFAULT_LATCH)
79 {
80 }
81 
82 
83 //-------------------------------------------------
84 //  device_config_complete - perform any
85 //  operations now that the configuration is
86 //  complete
87 //-------------------------------------------------
88 
device_config_complete()89 void cdp1864_device::device_config_complete()
90 {
91 	if (!has_screen())
92 		return;
93 
94 	if (!screen().refresh_attoseconds())
95 		screen().set_raw(clock(), SCREEN_WIDTH, HBLANK_END, HBLANK_START, TOTAL_SCANLINES, SCANLINE_VBLANK_END, SCANLINE_VBLANK_START);
96 
97 	if (!screen().has_screen_update())
98 		screen().set_screen_update(*this, FUNC(cdp1864_device::screen_update));
99 }
100 
101 
102 //-------------------------------------------------
103 //  device_start - device-specific startup
104 //-------------------------------------------------
105 
device_start()106 void cdp1864_device::device_start()
107 {
108 	// resolve callbacks
109 	m_read_inlace.resolve_safe(1);
110 	m_read_rdata.resolve_safe(0);
111 	m_read_bdata.resolve_safe(0);
112 	m_read_gdata.resolve_safe(0);
113 	m_write_int.resolve_safe();
114 	m_write_dma_out.resolve_safe();
115 	m_write_efx.resolve_safe();
116 	m_write_hsync.resolve_safe();
117 
118 	// initialize palette
119 	initialize_palette();
120 
121 	// create sound stream
122 	m_stream = stream_alloc(0, 1, SAMPLE_RATE_OUTPUT_ADAPTIVE);
123 
124 	// allocate timers
125 	m_int_timer = timer_alloc(TIMER_INT);
126 	m_efx_timer = timer_alloc(TIMER_EFX);
127 	m_dma_timer = timer_alloc(TIMER_DMA);
128 	m_hsync_timer = timer_alloc(TIMER_HSYNC);
129 
130 	// find devices
131 	screen().register_screen_bitmap(m_bitmap);
132 
133 	// register for state saving
134 	save_item(NAME(m_disp));
135 	save_item(NAME(m_dmaout));
136 	save_item(NAME(m_bgcolor));
137 	save_item(NAME(m_con));
138 	save_item(NAME(m_aoe));
139 	save_item(NAME(m_latch));
140 	save_item(NAME(m_signal));
141 	save_item(NAME(m_incr));
142 }
143 
144 
145 //-------------------------------------------------
146 //  device_reset - device-specific reset
147 //-------------------------------------------------
148 
device_reset()149 void cdp1864_device::device_reset()
150 {
151 	m_int_timer->adjust(screen().time_until_pos(SCANLINE_INT_START, 0));
152 	m_efx_timer->adjust(screen().time_until_pos(SCANLINE_EFX_TOP_START, 0));
153 	m_dma_timer->adjust(clocks_to_attotime(CDP1864_CYCLES_DMA_START));
154 
155 	m_disp = 0;
156 	m_dmaout = 0;
157 
158 	m_write_int(CLEAR_LINE);
159 	m_write_dma_out(CLEAR_LINE);
160 	m_write_efx(CLEAR_LINE);
161 	m_write_hsync(CLEAR_LINE);
162 }
163 
164 
165 //-------------------------------------------------
166 //  device_timer - handle timer events
167 //-------------------------------------------------
168 
device_timer(emu_timer & timer,device_timer_id id,int param,void * ptr)169 void cdp1864_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
170 {
171 	int scanline = screen().vpos();
172 
173 	switch (id)
174 	{
175 	case TIMER_INT:
176 		if (scanline == SCANLINE_INT_START)
177 		{
178 			if (m_disp)
179 			{
180 				m_write_int(ASSERT_LINE);
181 			}
182 
183 			m_int_timer->adjust(screen().time_until_pos(SCANLINE_INT_END, 0));
184 		}
185 		else
186 		{
187 			if (m_disp)
188 			{
189 				m_write_int(CLEAR_LINE);
190 			}
191 
192 			m_int_timer->adjust(screen().time_until_pos(SCANLINE_INT_START, 0));
193 		}
194 		break;
195 
196 	case TIMER_EFX:
197 		switch (scanline)
198 		{
199 		case SCANLINE_EFX_TOP_START:
200 			m_write_efx(ASSERT_LINE);
201 			m_efx_timer->adjust(screen().time_until_pos(SCANLINE_EFX_TOP_END, 0));
202 			break;
203 
204 		case SCANLINE_EFX_TOP_END:
205 			m_write_efx(CLEAR_LINE);
206 			m_efx_timer->adjust(screen().time_until_pos(SCANLINE_EFX_BOTTOM_START, 0));
207 			break;
208 
209 		case SCANLINE_EFX_BOTTOM_START:
210 			m_write_efx(ASSERT_LINE);
211 			m_efx_timer->adjust(screen().time_until_pos(SCANLINE_EFX_BOTTOM_END, 0));
212 			break;
213 
214 		case SCANLINE_EFX_BOTTOM_END:
215 			m_write_efx(CLEAR_LINE);
216 			m_efx_timer->adjust(screen().time_until_pos(SCANLINE_EFX_TOP_START, 0));
217 			break;
218 		}
219 		break;
220 
221 	case TIMER_DMA:
222 		if (m_dmaout)
223 		{
224 			if (m_disp)
225 			{
226 				if (scanline >= SCANLINE_DISPLAY_START && scanline < SCANLINE_DISPLAY_END)
227 				{
228 					m_write_dma_out(CLEAR_LINE);
229 				}
230 			}
231 
232 			m_dma_timer->adjust(clocks_to_attotime(CDP1864_CYCLES_DMA_WAIT));
233 
234 			m_dmaout = 0;
235 		}
236 		else
237 		{
238 			if (m_disp)
239 			{
240 				if (scanline >= SCANLINE_DISPLAY_START && scanline < SCANLINE_DISPLAY_END)
241 				{
242 					m_write_dma_out(ASSERT_LINE);
243 				}
244 			}
245 
246 			m_dma_timer->adjust(clocks_to_attotime(CDP1864_CYCLES_DMA_ACTIVE));
247 
248 			m_dmaout = 1;
249 		}
250 		break;
251 	}
252 }
253 
254 
255 //-------------------------------------------------
256 //  sound_stream_update - handle update requests for
257 //  our sound stream
258 //-------------------------------------------------
259 
sound_stream_update(sound_stream & stream,std::vector<read_stream_view> const & inputs,std::vector<write_stream_view> & outputs)260 void cdp1864_device::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs)
261 {
262 	stream_buffer::sample_t signal = m_signal;
263 	auto &buffer = outputs[0];
264 
265 	if (m_aoe)
266 	{
267 		double frequency = unscaled_clock() / 8 / 4 / (m_latch + 1) / 2;
268 		int rate = buffer.sample_rate() / 2;
269 
270 		/* get progress through wave */
271 		int incr = m_incr;
272 
273 		if (signal < 0)
274 		{
275 			signal = -1.0;
276 		}
277 		else
278 		{
279 			signal = 1.0;
280 		}
281 
282 		for (int sampindex = 0; sampindex < buffer.samples(); sampindex++)
283 		{
284 			buffer.put(sampindex, signal);
285 			incr -= frequency;
286 			while( incr < 0 )
287 			{
288 				incr += rate;
289 				signal = -signal;
290 			}
291 		}
292 
293 		/* store progress through wave */
294 		m_incr = incr;
295 		m_signal = signal;
296 	}
297 	else
298 		buffer.fill(0);
299 }
300 
301 
302 //-------------------------------------------------
303 //  dispon_r -
304 //-------------------------------------------------
305 
dispon_r()306 uint8_t cdp1864_device::dispon_r()
307 {
308 	m_disp = 1;
309 
310 	return 0xff;
311 }
312 
313 
314 //-------------------------------------------------
315 //  dispoff_r -
316 //-------------------------------------------------
317 
dispoff_r()318 uint8_t cdp1864_device::dispoff_r()
319 {
320 	m_disp = 0;
321 
322 	m_write_int(CLEAR_LINE);
323 	m_write_dma_out(CLEAR_LINE);
324 
325 	return 0xff;
326 }
327 
328 
329 //-------------------------------------------------
330 //  step_bgcolor_w -
331 //-------------------------------------------------
332 
step_bgcolor_w(uint8_t data)333 void cdp1864_device::step_bgcolor_w(uint8_t data)
334 {
335 	m_disp = 1;
336 
337 	m_bgcolor++;
338 	m_bgcolor &= 0x03;
339 }
340 
341 
342 //-------------------------------------------------
343 //  tone_latch_w -
344 //-------------------------------------------------
345 
tone_latch_w(uint8_t data)346 void cdp1864_device::tone_latch_w(uint8_t data)
347 {
348 	m_latch = data;
349 }
350 
351 
352 //-------------------------------------------------
353 //  dma_w -
354 //-------------------------------------------------
355 
dma_w(uint8_t data)356 void cdp1864_device::dma_w(uint8_t data)
357 {
358 	int rdata = 1, bdata = 1, gdata = 1;
359 	int sx = screen().hpos() + 4;
360 	int y = screen().vpos();
361 
362 	if (!m_con)
363 	{
364 		rdata = m_read_rdata();
365 		bdata = m_read_bdata();
366 		gdata = m_read_gdata();
367 	}
368 
369 	for (int x = 0; x < 8; x++)
370 	{
371 		int color = bckgnd[m_bgcolor] + 8;
372 
373 		if (BIT(data, 7))
374 		{
375 			color = (gdata << 2) | (bdata << 1) | rdata;
376 		}
377 
378 		m_bitmap.pix(y, sx + x) = m_palette[color];
379 
380 		data <<= 1;
381 	}
382 }
383 
384 
385 //-------------------------------------------------
386 //  con_w - color on write
387 //-------------------------------------------------
388 
con_w(int state)389 void cdp1864_device::con_w(int state)
390 {
391 	m_con = state;
392 }
393 
394 
395 //-------------------------------------------------
396 //  aoe_w - audio output enable write
397 //-------------------------------------------------
398 
aoe_w(int state)399 void cdp1864_device::aoe_w(int state)
400 {
401 	if (!state)
402 	{
403 		m_latch = CDP1864_DEFAULT_LATCH;
404 	}
405 
406 	m_aoe = state;
407 }
408 
409 
410 //-------------------------------------------------
411 //  evs_w - external vertical sync write
412 //-------------------------------------------------
413 
evs_w(int state)414 void cdp1864_device::evs_w(int state)
415 {
416 }
417 
418 
419 //-------------------------------------------------
420 //  update_screen -
421 //-------------------------------------------------
422 
screen_update(screen_device & screen,bitmap_rgb32 & bitmap,const rectangle & cliprect)423 uint32_t cdp1864_device::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
424 {
425 	if (m_disp)
426 	{
427 		copybitmap(bitmap, m_bitmap, 0, 0, 0, 0, cliprect);
428 		m_bitmap.fill(m_palette[bckgnd[m_bgcolor] + 8], cliprect);
429 	}
430 	else
431 	{
432 		bitmap.fill(rgb_t::black(), cliprect);
433 	}
434 
435 	return 0;
436 }
437 
438 
439 //-------------------------------------------------
440 //  initialize_palette -
441 //-------------------------------------------------
442 
initialize_palette()443 void cdp1864_device::initialize_palette()
444 {
445 	const int resistances_r[] = { static_cast<int>(m_chr_r) };
446 	const int resistances_g[] = { static_cast<int>(m_chr_g) };
447 	const int resistances_b[] = { static_cast<int>(m_chr_b) };
448 
449 	double color_weights_r[1], color_weights_g[1], color_weights_b[1];
450 	double color_weights_bkg_r[1], color_weights_bkg_g[1], color_weights_bkg_b[1];
451 
452 	compute_resistor_weights(0, 0xff, -1.0,
453 								1, resistances_r, color_weights_r, 0, m_chr_bkg,
454 								1, resistances_g, color_weights_g, 0, m_chr_bkg,
455 								1, resistances_b, color_weights_b, 0, m_chr_bkg);
456 
457 	compute_resistor_weights(0, 0xff, -1.0,
458 								1, resistances_r, color_weights_bkg_r, m_chr_bkg, 0,
459 								1, resistances_g, color_weights_bkg_g, m_chr_bkg, 0,
460 								1, resistances_b, color_weights_bkg_b, m_chr_bkg, 0);
461 
462 	for (int i = 0; i < 8; i++)
463 	{
464 		// foreground colors
465 		uint8_t r = 0, g = 0, b = 0;
466 
467 		if (m_chr_r != RES_INF) r = combine_weights(color_weights_r, BIT(i, 0));
468 		if (m_chr_b != RES_INF) b = combine_weights(color_weights_b, BIT(i, 1));
469 		if (m_chr_g != RES_INF) g = combine_weights(color_weights_g, BIT(i, 2));
470 
471 		m_palette[i] = rgb_t(r, g, b);
472 
473 		// background colors
474 		r = 0, g = 0, b = 0;
475 
476 		if (m_chr_r != RES_INF) r = combine_weights(color_weights_bkg_r, BIT(i, 0));
477 		if (m_chr_b != RES_INF) b = combine_weights(color_weights_bkg_b, BIT(i, 1));
478 		if (m_chr_g != RES_INF) g = combine_weights(color_weights_bkg_g, BIT(i, 2));
479 
480 		m_palette[i + 8] = rgb_t(r, g, b);
481 	}
482 }
483