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