1 // license:BSD-3-Clause
2 // copyright-holders:Curt Coder
3 #include "emu.h"
4 #include "ams40041.h"
5 #include "screen.h"
6 
7 #define VERBOSE 0
8 #include "logmacro.h"
9 
10 
11 //**************************************************************************
12 //  CONSTANTS
13 //**************************************************************************
14 
15 
16 static const rgb_t PALETTE_1512[] =
17 {
18 	rgb_t::black(),
19 	rgb_t(0x00, 0x00, 0xaa),
20 	rgb_t(0x00, 0xaa, 0x00),
21 	rgb_t(0x00, 0xaa, 0xaa),
22 	rgb_t(0xaa, 0x00, 0x00),
23 	rgb_t(0xaa, 0x00, 0xaa),
24 	rgb_t(0xaa, 0x55, 0x00),
25 	rgb_t(0xaa, 0xaa, 0xaa),
26 	rgb_t(0x55, 0x55, 0x55),
27 	rgb_t(0x55, 0x55, 0xff),
28 	rgb_t(0x55, 0xff, 0x55),
29 	rgb_t(0x55, 0xff, 0xff),
30 	rgb_t(0xff, 0x55, 0x55),
31 	rgb_t(0xff, 0x55, 0xff),
32 	rgb_t(0xff, 0xff, 0x55),
33 	rgb_t::white()
34 };
35 
36 static const int PALETTE_0[] = { 0, 3, 5, 7 };
37 static const int PALETTE_1[] = { 0, 2, 4, 6 };
38 static const int PALETTE_2[] = { 0, 3, 4, 7 };
39 
40 
41 enum
42 {
43 	ALPHA_40 = 0,
44 	ALPHA_80,
45 	GRAPHICS_1,
46 	GRAPHICS_2
47 };
48 
49 
50 #define MODE_ALPHA_80       0x01
51 #define MODE_GRAPHICS       0x02
52 #define MODE_PALETTE_2      0x04
53 #define MODE_ENABLE_VIDEO   0x08
54 #define MODE_GRAPHICS_2     0x10
55 #define MODE_BLINK          0x20
56 
57 
58 #define COLOR_INTENSITY     0x10
59 #define COLOR_PALETTE_1     0x20
60 
61 
62 #define VFP_LORES           22
63 #define HFP_LORES           16
64 
65 
66 
67 //**************************************************************************
68 //  AMS40041 DEVICE
69 //**************************************************************************
70 
71 DEFINE_DEVICE_TYPE(AMS40041, ams40041_device, "ams40041", "AMS40041 VDU")
72 
73 //-------------------------------------------------
74 //  ams40041_device - constructor
75 //-------------------------------------------------
76 
ams40041_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)77 ams40041_device::ams40041_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
78 	: mc6845_device(mconfig, AMS40041, tag, owner, clock)
79 	, m_char_rom(*this, DEVICE_SELF)
80 	, m_lk(*this, "^LK") // hack
81 {
82 	m_clk_scale = 32;
83 
84 	set_char_width(8);
85 	set_update_row_callback(*this, FUNC(ams40041_device::crtc_update_row));
86 }
87 
88 
89 //-------------------------------------------------
90 //  device_start - device-specific startup
91 //-------------------------------------------------
92 
device_start()93 void ams40041_device::device_start()
94 {
95 	mc6845_device::device_start();
96 
97 	m_horiz_char_total =  113;
98 	m_horiz_disp       =  80;
99 	m_horiz_sync_pos   =  90;
100 	m_sync_width       =  10;
101 	m_vert_char_total  =  127;
102 	m_vert_total_adj   =  6;
103 	m_vert_disp        =  100;
104 	m_vert_sync_pos    =  112;
105 	m_mode_control     =  2;
106 
107 	m_supports_disp_start_addr_r = false;
108 	m_supports_vert_sync_width = false;
109 	m_supports_status_reg_d5 = false;
110 	m_supports_status_reg_d6 = false;
111 	m_supports_status_reg_d7 = false;
112 	m_supports_transparent = false;
113 
114 	// allocate memory
115 	m_video_ram = make_unique_clear<uint8_t[]>(0x10000);
116 
117 	// state saving
118 	save_pointer(NAME(m_video_ram), 0x10000);
119 	save_item(NAME(m_toggle));
120 	save_item(NAME(m_lpen));
121 	save_item(NAME(m_blink));
122 	save_item(NAME(m_cursor));
123 	save_item(NAME(m_blink_ctr));
124 	save_item(NAME(m_vdu_mode));
125 	save_item(NAME(m_vdu_color));
126 	save_item(NAME(m_vdu_plane));
127 	save_item(NAME(m_vdu_rdsel));
128 	save_item(NAME(m_vdu_border));
129 }
130 
131 
132 //-------------------------------------------------
133 //  device_reset - device-specific reset
134 //-------------------------------------------------
135 
device_reset()136 void ams40041_device::device_reset()
137 {
138 	mc6845_device::device_reset();
139 
140 	m_toggle = 0;
141 	m_lpen = 0;
142 	m_blink = 0;
143 	m_cursor = 0;
144 	m_blink_ctr = 0;
145 	m_vdu_mode = 0;
146 	m_vdu_color = 0;
147 	m_vdu_rdsel = 0;
148 	m_vdu_plane = 0x0f;
149 	m_vdu_border = 0;
150 }
151 
152 
153 //**************************************************************************
154 //  VIDEO RAM ACCESS
155 //**************************************************************************
156 
157 //-------------------------------------------------
158 //  video_ram_r -
159 //-------------------------------------------------
160 
video_ram_r(offs_t offset)161 uint8_t ams40041_device::video_ram_r(offs_t offset)
162 {
163 	uint8_t data = 0;
164 
165 	switch (get_display_mode(m_vdu_mode))
166 	{
167 	case ALPHA_40:
168 	case ALPHA_80:
169 		data = m_video_ram[offset];
170 		break;
171 
172 	case GRAPHICS_1:
173 		data = m_video_ram[(offset << 2) | 3];
174 		break;
175 
176 	case GRAPHICS_2:
177 		data = m_video_ram[(offset << 2) | (m_vdu_rdsel ^ 0x03)];
178 		break;
179 	}
180 
181 	return data;
182 }
183 
184 
185 //-------------------------------------------------
186 //  video_ram_w -
187 //-------------------------------------------------
188 
video_ram_w(offs_t offset,uint8_t data)189 void ams40041_device::video_ram_w(offs_t offset, uint8_t data)
190 {
191 	switch (get_display_mode(m_vdu_mode))
192 	{
193 	case ALPHA_40:
194 	case ALPHA_80:
195 		m_video_ram[offset] = data;
196 		break;
197 
198 	case GRAPHICS_1:
199 		m_video_ram[(offset << 2) | 3] = data;
200 		m_video_ram[(offset << 2) | 2] = data;
201 		m_video_ram[(offset << 2) | 1] = data;
202 		m_video_ram[(offset << 2) | 0] = data;
203 		break;
204 
205 	case GRAPHICS_2:
206 		if (BIT(m_vdu_plane, 0)) m_video_ram[(offset << 2) | 3] = data;
207 		if (BIT(m_vdu_plane, 1)) m_video_ram[(offset << 2) | 2] = data;
208 		if (BIT(m_vdu_plane, 2)) m_video_ram[(offset << 2) | 1] = data;
209 		if (BIT(m_vdu_plane, 3)) m_video_ram[(offset << 2) | 0] = data;
210 		break;
211 	}
212 }
213 
214 
215 //-------------------------------------------------
216 //  vdu_r -
217 //-------------------------------------------------
218 
vdu_r(offs_t offset)219 uint8_t ams40041_device::vdu_r(offs_t offset)
220 {
221 	uint8_t data = 0;
222 
223 	switch (offset)
224 	{
225 	case 1: case 3: case 5: case 7:
226 		data = register_r();
227 		break;
228 
229 	case 0xa: // VDU Status
230 		/*
231 
232 		    bit     description
233 
234 		    0       Toggle Bit
235 		    1       Light-pen latch select
236 		    2       Light-pen switch off
237 		    3       Frame Flyback Time
238 		    4
239 		    5
240 		    6
241 		    7
242 
243 		*/
244 
245 		// toggle bit
246 		data |= m_toggle;
247 		m_toggle = !m_toggle;
248 
249 		// light pen latch
250 		data |= m_lpen << 1;
251 
252 		// light pen switch
253 		data |= 0x04;
254 
255 		// vertical sync
256 		//data |= vsync_r();
257 		int flyback = 0;
258 
259 		if (screen().vpos() < VFP_LORES - 16) flyback = 1;
260 		if (screen().vpos() > VFP_LORES + 200) flyback = 1;
261 
262 		data |= flyback << 3;
263 		break;
264 	}
265 
266 	return data;
267 }
268 
269 
270 //-------------------------------------------------
271 //  vdu_w -
272 //-------------------------------------------------
273 
vdu_w(offs_t offset,uint8_t data)274 void ams40041_device::vdu_w(offs_t offset, uint8_t data)
275 {
276 	switch (offset)
277 	{
278 	case 0: case 2: case 4: case 6:
279 		address_w(data);
280 		break;
281 
282 	case 1: case 3: case 5: case 7:
283 		register_w(data);
284 		break;
285 
286 	case 8: // VDU Mode Control
287 		/*
288 
289 		    bit     description
290 
291 		    0       Select Alpha 80 Char mode (de-select 40 Char mode)
292 		    1       Select Graphics modes (de-select Alpha modes)
293 		    2       Select Palette 2 (de-select palettes 0,1)
294 		    3       Enable Video Display
295 		    4       Select Graphics Mode 2 (de-select graphics mode 1)
296 		    5       Enable Blinking Chars (disable intensified backgrounds)
297 		    6
298 		    7
299 
300 		*/
301 
302 		LOG("VDU Mode Control %02x\n", data);
303 
304 		if ((get_display_mode(m_vdu_mode) != GRAPHICS_2) && (get_display_mode(data) == GRAPHICS_2))
305 		{
306 			m_vdu_plane = 0x0f;
307 			m_vdu_border = 0;
308 		}
309 
310 		if (get_display_mode(data) != GRAPHICS_2)
311 		{
312 			m_vdu_rdsel = 0;
313 		}
314 
315 		if (get_display_mode(m_vdu_mode) != get_display_mode(data))
316 		{
317 			switch (get_display_mode(data))
318 			{
319 			case ALPHA_40:
320 			case GRAPHICS_1:
321 				set_hpixels_per_column(8);
322 				m_clk_scale = 32;
323 				recompute_parameters(true);
324 				break;
325 
326 			case ALPHA_80:
327 				set_hpixels_per_column(8);
328 				m_clk_scale = 16;
329 				recompute_parameters(true);
330 				break;
331 
332 			case GRAPHICS_2:
333 				set_hpixels_per_column(16);
334 				m_clk_scale = 32;
335 				recompute_parameters(true);
336 				break;
337 			}
338 		}
339 
340 		m_vdu_mode = data;
341 		break;
342 
343 	case 9: // VDU Colour Select
344 		/*
345 
346 		    bit     description
347 
348 		    0
349 		    1
350 		    2
351 		    3
352 		    4
353 		    5
354 		    6
355 		    7
356 
357 		*/
358 
359 		LOG("VDU Colour Select %02x\n", data);
360 
361 		m_vdu_color = data;
362 		break;
363 
364 	case 0xb: // Clear Light Pen Latch
365 		LOG("VDU Clear Light Pen Latch\n");
366 
367 		m_lpen = 0;
368 		break;
369 
370 	case 0xc: // Set Light Pen Latch
371 		LOG("VDU Set Light Pen Latch\n");
372 
373 		if (!m_lpen)
374 		{
375 			assert_light_pen_input();
376 		}
377 
378 		m_lpen = 1;
379 		break;
380 
381 	case 0xd: // VDU Colour Plane Write
382 		/*
383 
384 		    bit     description
385 
386 		    0       Allow CPU write to Blue Plane
387 		    1       Allow CPU write to Green Plane
388 		    2       Allow CPU write to Red Plane
389 		    3       Allow CPU write to Intensity Plane
390 		    4
391 		    5
392 		    6
393 		    7
394 
395 		*/
396 
397 		LOG("VDU Colour Plane Write %01x\n", data & 0x0f);
398 
399 		if (get_display_mode(m_vdu_mode) == GRAPHICS_2)
400 		{
401 			m_vdu_plane = data;
402 		}
403 		break;
404 
405 	case 0xe: // VDU Colour Plane Read
406 		/*
407 
408 		    bit     description
409 
410 		    0       Read Select bit 0 (RDSEL0)
411 		    1       Read Select bit 1 (RDSEL1)
412 		    2
413 		    3
414 		    4
415 		    5
416 		    6
417 		    7
418 
419 		*/
420 
421 		LOG("VDU Colour Plane Read %u\n", data & 0x03);
422 
423 		if (get_display_mode(m_vdu_mode) == GRAPHICS_2)
424 		{
425 			m_vdu_rdsel = data & 0x03;
426 		}
427 		break;
428 
429 	case 0xf: // VDU Graphics Mode 2 Border
430 		/*
431 
432 		    bit     description
433 
434 		    0       Border Blue
435 		    1       Border Green
436 		    2       Border Red
437 		    3       Border Intensity
438 		    4
439 		    5
440 		    6
441 		    7
442 
443 		*/
444 
445 		LOG("VDU Graphics Mode 2 Border %u\n", data & 0x0f);
446 
447 		m_vdu_border = data;
448 		break;
449 	}
450 }
451 
452 
453 //-------------------------------------------------
454 //  mc6845
455 //-------------------------------------------------
456 
get_display_mode(uint8_t mode)457 int ams40041_device::get_display_mode(uint8_t mode)
458 {
459 	if (mode & MODE_GRAPHICS)
460 	{
461 		if (mode & MODE_GRAPHICS_2)
462 		{
463 			return GRAPHICS_2;
464 		}
465 		else
466 		{
467 			return GRAPHICS_1;
468 		}
469 	}
470 	else
471 	{
472 		if (mode & MODE_ALPHA_80)
473 		{
474 			return ALPHA_80;
475 		}
476 		else
477 		{
478 			return ALPHA_40;
479 		}
480 	}
481 }
482 
get_char_rom_offset()483 offs_t ams40041_device::get_char_rom_offset()
484 {
485 	return ((m_lk->read() >> 5) & 0x03) << 11;
486 }
487 
MC6845_UPDATE_ROW(ams40041_device::draw_alpha)488 MC6845_UPDATE_ROW( ams40041_device::draw_alpha )
489 {
490 	offs_t char_rom_offset = get_char_rom_offset();
491 	uint32_t *p = &bitmap.pix(y + vbp, hbp);
492 
493 	if (get_display_mode(m_vdu_mode) == ALPHA_40)
494 		p = &bitmap.pix(y + vbp, hbp);
495 
496 	if (y > 199) return;
497 
498 	for (int column = 0; column < x_count; column++)
499 	{
500 		uint8_t code = m_video_ram[(ma + column) << 1];
501 		uint8_t attr = m_video_ram[((ma + column) << 1) + 1];
502 		int fg = attr & 0x0f;
503 		int bg = attr >> 4;
504 
505 		if (m_vdu_mode & MODE_BLINK)
506 		{
507 			bg &= 0x07;
508 
509 			if (BIT(attr, 7) && !m_blink)
510 			{
511 				fg = bg;
512 			}
513 		}
514 
515 		offs_t addr = char_rom_offset | (code << 3) | (ra & 0x07);
516 		uint8_t data = m_char_rom[addr & 0x1fff];
517 
518 		if ((column == cursor_x) && m_cursor)
519 		{
520 			data = 0xff;
521 		}
522 
523 		for (int bit = 0; bit < 8; bit++)
524 		{
525 			int color = BIT(data, 7) ? fg : bg;
526 
527 			*p = PALETTE_1512[de ? color : 0]; p++;
528 
529 			data <<= 1;
530 		}
531 	}
532 }
533 
get_color(uint8_t data)534 int ams40041_device::get_color(uint8_t data)
535 {
536 	if (data == 0) return m_vdu_color & 0x0f;
537 
538 	int color = PALETTE_0[data & 0x03];
539 
540 	if (m_vdu_color & COLOR_PALETTE_1)
541 	{
542 		color = PALETTE_1[data & 0x03];
543 	}
544 	else if (m_vdu_mode & MODE_PALETTE_2)
545 	{
546 		color = PALETTE_2[data & 0x03];
547 	}
548 
549 	if (m_vdu_color & COLOR_INTENSITY)
550 	{
551 		color += 8;
552 	}
553 
554 	return color;
555 }
556 
MC6845_UPDATE_ROW(ams40041_device::draw_graphics_1)557 MC6845_UPDATE_ROW( ams40041_device::draw_graphics_1 )
558 {
559 	if (y > 199) return;
560 
561 	uint32_t *p = &bitmap.pix(y + vbp, hbp);
562 
563 	for (int column = 0; column < x_count; column++)
564 	{
565 		offs_t offset = ((ra & 0x01) << 15) | ((ma + column) << 3);
566 
567 		uint16_t b = (m_video_ram[offset | 3] << 8) | m_video_ram[offset | 7];
568 
569 		for (int x = 0; x < 8; x++)
570 		{
571 			*p = PALETTE_1512[de ? get_color((BIT(b, 15) << 1) | BIT(b, 14)) : 0]; p++;
572 			b <<= 2;
573 		}
574 	}
575 }
576 
MC6845_UPDATE_ROW(ams40041_device::draw_graphics_2)577 MC6845_UPDATE_ROW( ams40041_device::draw_graphics_2 )
578 {
579 	if (y > 199) return;
580 
581 	uint32_t *p = &bitmap.pix(y + vbp, hbp);
582 
583 	for (int column = 0; column < x_count; column++)
584 	{
585 		offs_t offset = ((ra & 0x01) << 15) | ((ma + column) << 3);
586 
587 		uint16_t i = BIT(m_vdu_color, 3) ? ((m_video_ram[offset | 0] << 8) | m_video_ram[offset | 4]) : 0;
588 		uint16_t r = BIT(m_vdu_color, 2) ? ((m_video_ram[offset | 1] << 8) | m_video_ram[offset | 5]) : 0;
589 		uint16_t g = BIT(m_vdu_color, 1) ? ((m_video_ram[offset | 2] << 8) | m_video_ram[offset | 6]) : 0;
590 		uint16_t b = BIT(m_vdu_color, 0) ? ((m_video_ram[offset | 3] << 8) | m_video_ram[offset | 7]) : 0;
591 
592 		for (int x = 0; x < 16; x++)
593 		{
594 			*p = PALETTE_1512[de ? (BIT(i, 15) << 3) | (BIT(r, 15) << 2) | (BIT(g, 15) << 1) | BIT(b, 15) : 0]; p++;
595 			i <<= 1; r <<= 1; g <<= 1; b <<= 1;
596 		}
597 	}
598 }
599 
MC6845_UPDATE_ROW(ams40041_device::crtc_update_row)600 MC6845_UPDATE_ROW( ams40041_device::crtc_update_row )
601 {
602 	switch (get_display_mode(m_vdu_mode))
603 	{
604 	case ALPHA_40:
605 	case ALPHA_80:
606 		draw_alpha(bitmap, cliprect, ma, ra, y, x_count, cursor_x, de, hbp, vbp);
607 		break;
608 
609 	case GRAPHICS_1:
610 		draw_graphics_1(bitmap, cliprect, ma, ra, y, x_count, cursor_x, de, hbp, vbp);
611 		break;
612 
613 	case GRAPHICS_2:
614 		draw_graphics_2(bitmap, cliprect, ma, ra, y, x_count, cursor_x, de, hbp, vbp);
615 		break;
616 	}
617 }
618 
619 
screen_update(screen_device & screen,bitmap_rgb32 & bitmap,const rectangle & cliprect)620 uint32_t ams40041_device::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
621 {
622 	if (m_vdu_mode & MODE_ENABLE_VIDEO)
623 	{
624 		m_blink_ctr++;
625 
626 		if (m_blink_ctr == 0x08)
627 		{
628 			m_cursor = !m_cursor;
629 		}
630 		else if (m_blink_ctr == 0x10)
631 		{
632 			m_cursor = !m_cursor;
633 			m_blink = !m_blink;
634 			m_blink_ctr = 0;
635 		}
636 
637 		switch (get_display_mode(m_vdu_mode))
638 		{
639 		case ALPHA_40:
640 		case GRAPHICS_1:
641 			screen.set_visible_area(0, 359, 0, 245);
642 			break;
643 
644 		case ALPHA_80:
645 		case GRAPHICS_2:
646 			screen.set_visible_area(0, 831, 0, 245);
647 			break;
648 		}
649 
650 		switch (get_display_mode(m_vdu_mode))
651 		{
652 		case ALPHA_40:
653 		case ALPHA_80:
654 		case GRAPHICS_1:
655 			bitmap.fill(PALETTE_1512[m_vdu_color & 0x0f], cliprect);
656 			break;
657 
658 		case GRAPHICS_2:
659 			bitmap.fill(PALETTE_1512[m_vdu_border & 0x0f], cliprect);
660 			break;
661 		}
662 
663 		mc6845_device::screen_update(screen, bitmap, cliprect);
664 	}
665 	else
666 	{
667 		bitmap.fill(rgb_t::black(), cliprect);
668 	}
669 
670 	return 0;
671 }
672