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