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