1 // license:BSD-3-Clause
2 // copyright-holders:Curt Coder
3 /**********************************************************************
4 
5     VideoBrain UV201/UV202 video chip emulation
6 
7 **********************************************************************/
8 
9 #include "emu.h"
10 #include "uv201.h"
11 
12 
13 
14 //**************************************************************************
15 //  MACROS / CONSTANTS
16 //**************************************************************************
17 
18 #define LOG         1
19 
20 
21 // screen parameters
22 #define SCREEN_WIDTH                232
23 #define SCREEN_HEIGHT               262
24 #define VISAREA_WIDTH               193
25 #define VBLANK_WIDTH                21
26 #define HBLANK_WIDTH                39
27 #define HSYNC_WIDTH                 18
28 #define HFP_WIDTH                   16
29 #define HBP_WIDTH                   5
30 #define HBLANK_END                  HSYNC_WIDTH + HFP_WIDTH
31 #define HBLANK_START                HBLANK_END + VISAREA_WIDTH
32 
33 
34 // write-only registers
35 #define REGISTER_COMMAND            0xf7
36 #define REGISTER_BACKGROUND         0xf5
37 #define REGISTER_FINAL_MODIFIER     0xf2
38 #define REGISTER_Y_INTERRUPT        0xf0
39 
40 
41 // read-only registers
42 #define REGISTER_X_FREEZE           0xf8
43 #define REGISTER_Y_FREEZE_LOW       0xf9
44 #define REGISTER_Y_FREEZE_HIGH      0xfa
45 #define REGISTER_CURRENT_Y_LOW      0xfb
46 
47 
48 // read/write registers - RAM memory
49 #define RAM_RP_LO                   0x00    // cartridge pointer low order
50 #define RAM_RP_HI_COLOR             0x10    // cartridge pointer high order and color
51 #define RAM_DX_INT_XCOPY            0x20    // dX, intensity, X-copy
52 #define RAM_DY                      0x30    // dY
53 #define RAM_X                       0x40    // X value
54 #define RAM_Y_LO_A                  0x50    // Y value low order list A
55 #define RAM_Y_LO_B                  0x60    // Y value low order list B
56 #define RAM_XY_HI_A                 0x70    // Y value high order and X order list A
57 #define RAM_XY_HI_B                 0x80    // Y value high order and X order list B
58 
59 
60 // command register bits
61 #define COMMAND_YINT_H_O            0x80
62 #define COMMAND_A_B                 0x40
63 #define COMMAND_Y_ZM                0x20
64 #define COMMAND_KBD                 0x10
65 #define COMMAND_INT                 0x08
66 #define COMMAND_ENB                 0x04
67 #define COMMAND_FRZ                 0x02
68 #define COMMAND_X_ZM                0x01
69 
70 
71 #define IS_CHANGED(_bit) \
72 	((m_cmd & _bit) != (data & _bit))
73 
74 #define RAM(_offset) \
75 	m_ram[_offset + i]
76 
77 #define RAM_XORD(_offset) \
78 	m_ram[_offset + xord]
79 
80 #define IS_VISIBLE(_y) \
81 	((_y >= cliprect.min_y) && (_y <= cliprect.max_y))
82 
83 #define DRAW_PIXEL(_scanline, _dot) \
84 	if (IS_VISIBLE(_scanline)) bitmap.pix((_scanline), HSYNC_WIDTH + HFP_WIDTH + _dot) = m_palette_val[pixel];
85 
86 
87 
88 //**************************************************************************
89 //  LIVE DEVICE
90 //**************************************************************************
91 
92 // device type definition
93 DEFINE_DEVICE_TYPE(UV201, uv201_device, "uv201", "UV201")
94 
95 
96 //-------------------------------------------------
97 //  uv201_device - constructor
98 //-------------------------------------------------
99 
uv201_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)100 uv201_device::uv201_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
101 	device_t(mconfig, UV201, tag, owner, clock),
102 	device_video_interface(mconfig, *this),
103 	m_write_ext_int(*this),
104 	m_write_hblank(*this),
105 	m_read_db(*this)
106 {
107 }
108 
109 
110 //-------------------------------------------------
111 //  device_start - device-specific startup
112 //-------------------------------------------------
113 
device_start()114 void uv201_device::device_start()
115 {
116 	// resolve callbacks
117 	m_write_ext_int.resolve_safe();
118 	m_write_hblank.resolve_safe();
119 	m_read_db.resolve_safe(0);
120 
121 	// allocate timers
122 	m_timer_y_odd = timer_alloc(TIMER_Y_ODD);
123 	m_timer_y_even = timer_alloc(TIMER_Y_EVEN);
124 	m_timer_hblank_on = timer_alloc(TIMER_HBLANK_ON);
125 	m_timer_hblank_off = timer_alloc(TIMER_HBLANK_OFF);
126 
127 	initialize_palette();
128 
129 	memset(m_ram, 0x00, sizeof(m_ram));
130 	m_y_int = 0;
131 	m_fmod = 0;
132 	m_bg = 0;
133 	m_cmd = 0;
134 	m_freeze_x = 0;
135 	m_freeze_y = 0;
136 	m_field = 0;
137 
138 	// state saving
139 	save_item(NAME(m_ram));
140 	save_item(NAME(m_y_int));
141 	save_item(NAME(m_fmod));
142 	save_item(NAME(m_bg));
143 	save_item(NAME(m_cmd));
144 	save_item(NAME(m_freeze_x));
145 	save_item(NAME(m_freeze_y));
146 	save_item(NAME(m_field));
147 }
148 
149 
150 //-------------------------------------------------
151 //  device_reset - device-specific reset
152 //-------------------------------------------------
153 
device_reset()154 void uv201_device::device_reset()
155 {
156 	m_write_ext_int(CLEAR_LINE);
157 
158 	m_write_hblank(1);
159 	m_timer_hblank_off->adjust(attotime::from_ticks( HBLANK_END, m_clock ));
160 }
161 
162 
163 //-------------------------------------------------
164 //  device_timer - handle timer events
165 //-------------------------------------------------
166 
device_timer(emu_timer & timer,device_timer_id id,int param,void * ptr)167 void uv201_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
168 {
169 	int scanline = screen().vpos();
170 
171 	switch (id)
172 	{
173 	case TIMER_Y_ODD:
174 	case TIMER_Y_EVEN:
175 		if ((m_cmd & COMMAND_INT) && !(m_cmd & COMMAND_FRZ))
176 		{
177 			if (LOG) logerror("Y-Interrupt at scanline %u\n", scanline);
178 
179 			m_freeze_y = scanline;
180 
181 			m_write_ext_int(ASSERT_LINE);
182 			m_write_ext_int(CLEAR_LINE);
183 		}
184 		break;
185 
186 	case TIMER_HBLANK_ON:
187 		m_write_hblank(1);
188 
189 		m_timer_hblank_off->adjust(attotime::from_ticks( HBLANK_WIDTH, m_clock ) );
190 		break;
191 
192 	case TIMER_HBLANK_OFF:
193 		m_write_hblank(0);
194 
195 		m_timer_hblank_on->adjust(attotime::from_ticks( VISAREA_WIDTH, m_clock ) );
196 		break;
197 	}
198 }
199 
200 
201 //-------------------------------------------------
202 //  initialize_palette -
203 //-------------------------------------------------
204 
initialize_palette()205 void uv201_device::initialize_palette()
206 {
207 	uint8_t offlointensity = 0x00;
208 	uint8_t offhiintensity = 0xc0;
209 
210 	uint8_t onlointensity = 0xa0;
211 	uint8_t onhiintensity = 0xff;
212 
213 	for (int i = 0; i < 4; i++)
214 	{
215 		int offset = i * 8;
216 		uint8_t onvalue, offvalue;
217 
218 		if (offset < 16)
219 		{
220 			offvalue = offlointensity;
221 			onvalue = onlointensity;
222 		}
223 		else
224 		{
225 			offvalue = offhiintensity;
226 			onvalue = onhiintensity;
227 		}
228 
229 		m_palette_val[offset + 0] = rgb_t(offvalue, offvalue, offvalue); // black
230 		m_palette_val[offset + 1] = rgb_t(onvalue, offvalue, offvalue); // red
231 		m_palette_val[offset + 2] = rgb_t(offvalue, onvalue, offvalue); // green
232 		m_palette_val[offset + 3] = rgb_t(onvalue, onvalue, offvalue); // red-green
233 		m_palette_val[offset + 4] = rgb_t(offvalue, offvalue, onvalue); // blue
234 		m_palette_val[offset + 5] = rgb_t(onvalue, offvalue, onvalue); // red-blue
235 		m_palette_val[offset + 6] = rgb_t(offvalue, onvalue, onvalue); // green-blue
236 		m_palette_val[offset + 7] = rgb_t(onvalue, onvalue, onvalue); // white
237 	}
238 }
239 
240 
241 //-------------------------------------------------
242 //  get_field_vpos - get scanline within field
243 //-------------------------------------------------
244 
get_field_vpos()245 int uv201_device::get_field_vpos()
246 {
247 	int vpos = screen().vpos();
248 
249 	if (vpos >= SCREEN_HEIGHT)
250 	{
251 		// even field
252 		vpos -= SCREEN_HEIGHT;
253 	}
254 
255 	return vpos;
256 }
257 
258 
259 //-------------------------------------------------
260 //  get_field - get video field
261 //-------------------------------------------------
262 
get_field()263 int uv201_device::get_field()
264 {
265 	return screen().vpos() < SCREEN_HEIGHT;
266 }
267 
268 
269 //-------------------------------------------------
270 //  set_y_interrupt - set Y interrupt timer
271 //-------------------------------------------------
272 
set_y_interrupt()273 void uv201_device::set_y_interrupt()
274 {
275 	int scanline = ((m_cmd & COMMAND_YINT_H_O) << 1) | m_y_int;
276 
277 	m_timer_y_odd->adjust(screen().time_until_pos(scanline), 0, screen().frame_period());
278 	//m_timer_y_even->adjust(screen().time_until_pos(scanline + SCREEN_HEIGHT), 0, screen().frame_period());
279 }
280 
281 
282 //-------------------------------------------------
283 //  do_partial_update - update screen
284 //-------------------------------------------------
285 
do_partial_update()286 void uv201_device::do_partial_update()
287 {
288 	int vpos = screen().vpos();
289 
290 	if (LOG) logerror("Partial screen update at scanline %u\n", vpos);
291 
292 	screen().update_partial(vpos);
293 }
294 
295 
296 //-------------------------------------------------
297 //  read -
298 //-------------------------------------------------
299 
read(offs_t offset)300 uint8_t uv201_device::read(offs_t offset)
301 {
302 	uint8_t data = 0xff;
303 
304 	switch (offset)
305 	{
306 	case REGISTER_X_FREEZE:
307 		data = m_freeze_x;
308 
309 		if (LOG) logerror("X-Freeze %02x\n", data);
310 		break;
311 
312 	case REGISTER_Y_FREEZE_LOW:
313 		data = m_freeze_y & 0xff;
314 
315 		if (LOG) logerror("Y-Freeze Low %02x\n", data);
316 		break;
317 
318 	case REGISTER_Y_FREEZE_HIGH:
319 		/*
320 
321 		    bit     signal      description
322 
323 		    0       Y-F8        Y freeze high order (MSB) bit
324 		    1       Y-C8        current Y counter high order (MSB) bit
325 		    2
326 		    3
327 		    4
328 		    5
329 		    6
330 		    7       O/_E        odd/even field
331 
332 		*/
333 
334 		data = (get_field() << 7) | (BIT(get_field_vpos(), 8) << 1) | BIT(m_freeze_y, 8);
335 
336 		if (LOG) logerror("Y-Freeze High %02x\n", data);
337 		break;
338 
339 	case REGISTER_CURRENT_Y_LOW:
340 		data = get_field_vpos() & 0xff;
341 
342 		if (LOG) logerror("Current-Y Low %02x\n", data);
343 		break;
344 
345 	default:
346 		if (offset < 0x90)
347 			data = m_ram[offset];
348 		else
349 			if (LOG) logerror("Unknown VLSI read from %02x!\n", offset);
350 	}
351 
352 	return data;
353 }
354 
355 
356 //-------------------------------------------------
357 //  write -
358 //-------------------------------------------------
359 
write(offs_t offset,uint8_t data)360 void uv201_device::write(offs_t offset, uint8_t data)
361 {
362 	switch (offset)
363 	{
364 	case REGISTER_Y_INTERRUPT:
365 		if (LOG) logerror("Y-Interrupt %02x\n", data);
366 
367 		if (m_y_int != data)
368 		{
369 			m_y_int = data;
370 			set_y_interrupt();
371 		}
372 		break;
373 
374 	case REGISTER_FINAL_MODIFIER:
375 		/*
376 
377 		    bit     signal      description
378 
379 		    0       RED         red
380 		    1       GREEN       green
381 		    2       BLUE        blue
382 		    3       INT 0       intensity 0
383 		    4       INT 1       intensity 1
384 		    5       not used
385 		    6       not used
386 		    7       not used
387 
388 		*/
389 
390 		if (LOG) logerror("Final Modifier %02x\n", data);
391 
392 		do_partial_update();
393 		m_fmod = data & 0x1f;
394 		break;
395 
396 	case REGISTER_BACKGROUND:
397 		/*
398 
399 		    bit     signal      description
400 
401 		    0       RED         red
402 		    1       GREEN       green
403 		    2       BLUE        blue
404 		    3       INT 0       intensity 0
405 		    4       INT 1       intensity 1
406 		    5       not used
407 		    6       not used
408 		    7       not used
409 
410 		*/
411 
412 		if (LOG) logerror("Background %02x\n", data);
413 
414 		do_partial_update();
415 		m_bg = data & 0x1f;
416 		break;
417 
418 	case REGISTER_COMMAND:
419 		/*
420 
421 		    bit     signal      description
422 
423 		    0       X-ZM        X zoom
424 		    1       FRZ         freeze
425 		    2       ENB         video enable
426 		    3       INT         interrupt enable
427 		    4       KBD         general purpose output
428 		    5       Y-ZM        Y zoom
429 		    6       A/_B        list selection
430 		    7       YINT H.O.   Y COMMAND_INT register high order bit
431 
432 		*/
433 
434 		if (LOG) logerror("Command %02x\n", data);
435 
436 		if (IS_CHANGED(COMMAND_YINT_H_O))
437 		{
438 			set_y_interrupt();
439 		}
440 
441 		if (IS_CHANGED(COMMAND_A_B) || IS_CHANGED(COMMAND_Y_ZM) || IS_CHANGED(COMMAND_X_ZM))
442 		{
443 			do_partial_update();
444 		}
445 
446 		m_cmd = data;
447 		break;
448 
449 	default:
450 		if (offset < 0x90)
451 			m_ram[offset] = data;
452 		else
453 			logerror("Unknown VLSI write %02x to %02x!\n", data, offset);
454 	}
455 }
456 
457 
458 //-------------------------------------------------
459 //  ext_int_w - external interrupt write
460 //-------------------------------------------------
461 
WRITE_LINE_MEMBER(uv201_device::ext_int_w)462 WRITE_LINE_MEMBER( uv201_device::ext_int_w )
463 {
464 	if (!state && (m_cmd & COMMAND_FRZ))
465 	{
466 		m_freeze_y = get_field_vpos();
467 		m_freeze_x = screen().hpos();
468 	}
469 }
470 
471 
472 //-------------------------------------------------
473 //  kbd_r - keyboard select read
474 //-------------------------------------------------
475 
READ_LINE_MEMBER(uv201_device::kbd_r)476 READ_LINE_MEMBER( uv201_device::kbd_r )
477 {
478 	return (m_cmd & COMMAND_KBD) ? 1 : 0;
479 }
480 
481 
482 //-------------------------------------------------
483 //  screen_update -
484 //-------------------------------------------------
485 
screen_update(screen_device & screen,bitmap_rgb32 & bitmap,const rectangle & cliprect)486 uint32_t uv201_device::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
487 {
488 	bitmap.fill(rgb_t(0x00,0x00,0x00), cliprect);
489 
490 	if (!(m_cmd & COMMAND_ENB))
491 	{
492 		return 0;
493 	}
494 
495 	for (int y = 0; y < SCREEN_HEIGHT; y++)
496 	{
497 		for (int x = 0; x < VISAREA_WIDTH; x++)
498 		{
499 			int pixel = m_bg;
500 			DRAW_PIXEL(y, x);
501 		}
502 	}
503 
504 	for (int i = 0; i < 16; i++)
505 	{
506 		uint8_t xy_hi = (m_cmd & COMMAND_A_B) ? RAM(RAM_XY_HI_A) : RAM(RAM_XY_HI_B);
507 		uint8_t y_lo = (m_cmd & COMMAND_A_B) ? RAM(RAM_Y_LO_A) : RAM(RAM_Y_LO_B);
508 		uint16_t y = (BIT(xy_hi, 7) << 8) | y_lo;
509 		int xord = xy_hi & 0x0f;
510 
511 		uint8_t rp_hi_color = RAM_XORD(RAM_RP_HI_COLOR);
512 		uint8_t rp_lo = RAM_XORD(RAM_RP_LO);
513 		uint16_t rp = ((rp_hi_color << 8) | rp_lo) & 0x1fff;
514 
515 		if (rp < 0x800) rp |= 0x2000;
516 
517 		uint8_t dx_int_xcopy = RAM_XORD(RAM_DX_INT_XCOPY);
518 		int color = ((dx_int_xcopy & 0x60) >> 2) | (BIT(rp_hi_color, 5) << 2) | (BIT(rp_hi_color, 6) << 1) | (BIT(rp_hi_color, 7));
519 		uint8_t dx = dx_int_xcopy & 0x1f;
520 		uint8_t dy = RAM_XORD(RAM_DY);
521 		int xcopy = BIT(dx_int_xcopy, 7);
522 		uint8_t x = RAM_XORD(RAM_X);
523 
524 		if (LOG) logerror("Object %u xord %u y %u x %u dy %u dx %u xcopy %u color %u rp %04x\n", i, xord, y, x, dy, dx, xcopy, color, rp);
525 
526 		if (rp == 0) continue;
527 		if (y > SCREEN_HEIGHT) continue;
528 
529 		for (int sy = 0; sy < dy; sy++)
530 		{
531 			for (int sx = 0; sx < dx; sx++)
532 			{
533 				uint8_t data = m_read_db(rp);
534 
535 				for (int bit = 0; bit < 8; bit++)
536 				{
537 					int pixel = ((BIT(data, 7) ? color : m_bg) ^ m_fmod) & 0x1f;
538 
539 					if (m_cmd & COMMAND_Y_ZM)
540 					{
541 						int scanline = y + (sy * 2);
542 
543 						if (m_cmd & COMMAND_X_ZM)
544 						{
545 							int dot = (x * 2) + (sx * 16) + (bit * 2);
546 
547 							DRAW_PIXEL(scanline, dot);
548 							DRAW_PIXEL(scanline, dot + 1);
549 							DRAW_PIXEL(scanline + 1, dot);
550 							DRAW_PIXEL(scanline + 1, dot + 1);
551 						}
552 						else
553 						{
554 							int dot = x + (sx * 8) + bit;
555 
556 							DRAW_PIXEL(scanline, dot);
557 							DRAW_PIXEL(scanline + 1, dot);
558 						}
559 					}
560 					else
561 					{
562 						int scanline = y + sy;
563 
564 						if (m_cmd & COMMAND_X_ZM)
565 						{
566 							int dot = (x * 2) + (sx * 16) + (bit * 2);
567 
568 							DRAW_PIXEL(scanline, dot);
569 							DRAW_PIXEL(scanline, dot + 1);
570 						}
571 						else
572 						{
573 							int dot = x + (sx * 8) + bit;
574 
575 							DRAW_PIXEL(scanline, dot);
576 						}
577 					}
578 
579 					data <<= 1;
580 				}
581 
582 				if (!xcopy) rp++;
583 			}
584 
585 			if (xcopy) rp++;
586 		}
587 	}
588 
589 	return 0;
590 }
591