trunk/src/mess/video/mc6847.c
| r20796 | r20797 | |
| 31 | 31 | suggestions. Mismatching modes is responsible for the semigraphic modes |
| 32 | 32 | on the CoCo. |
| 33 | 33 | |
| 34 | | Timing: |
| 35 | | (source Motorola M6847 Manual) |
| 34 | Timing: (source Motorola M6847 Manual, experimentation, SockMaster) |
| 36 | 35 | |
| 37 | | Horizontal Sync: Total Period: 227.5 clock cycles |
| 36 | Horizontal Sync: Total Period: 228 clock cycles |
| 38 | 37 | @ CLK(0) + DHS_F - falling edge (high to low) |
| 39 | 38 | @ CLK(16.5) + DHS_R - rising edge (low to high) |
| 40 | 39 | @ CLK(42) - left border start |
| 41 | 40 | @ CLK(71.5) - body start |
| 42 | 41 | @ CLK(199.5) - right border start |
| 43 | | @ CLK(227.5) + DHS_F - falling edge (high to low) |
| 42 | @ CLK(228) + DHS_F - falling edge (high to low) |
| 44 | 43 | ... |
| 45 | 44 | |
| 46 | | Field Sync: Total Period 262*227.5 clock cycles |
| 45 | Field Sync: Total Period 262*228 clock cycles |
| 47 | 46 | @ CLK(0) + DFS_F - falling edge (high to low) |
| 48 | | @ CLK(32*227.5) + DFS_R - rising edge (low to high) |
| 49 | | @ CLK(262*227.5) + DFS_F - falling edge (high to low) (262.5 for the M6847Y) |
| 47 | @ CLK(32*228) + DFS_R - rising edge (low to high) |
| 48 | @ CLK(262*228) + DFS_F - falling edge (high to low) (262.5 for the M6847Y) |
| 50 | 49 | |
| 51 | 50 | DHS_F: 550ns |
| 52 | 51 | DHS_R: 740ns |
| r20796 | r20797 | |
| 59 | 58 | assuming that the extra text modes on the CoCo 2B are activated by the |
| 60 | 59 | GM2-0 pins. This needs to be confirmed. |
| 61 | 60 | |
| 61 | The MC6847 datasheet states that a scanline is 227.5 clock cycles, |
| 62 | but experimentation suggests that it is 228. The game "Dragon Fire" |
| 63 | has a fine tuned loop that runs in 57 clock cycles by the CPU's |
| 64 | reckoning (228 actual clock cycles) and would not function correctly |
| 65 | if skew existed. SockMaster has confirmed that scanlines are in |
| 66 | fact 228 clock cycles. |
| 67 | |
| 62 | 68 | **********************************************************************/ |
| 63 | 69 | |
| 64 | 70 | |
| r20796 | r20797 | |
| 73 | 79 | #define TOP_BORDER 25 |
| 74 | 80 | #define USE_HORIZONTAL_CLIP false |
| 75 | 81 | |
| 76 | | #define TIMER_HSYNC_PERIOD (227.5) |
| 82 | #define TIMER_HSYNC_PERIOD (228) |
| 77 | 83 | #define TIMER_HSYNC_OFF_TIME (10.0) |
| 78 | 84 | #define TIMER_HSYNC_ON_TIME (TIMER_HSYNC_OFF_TIME + 16.5) |
| 79 | 85 | #define TIMER_FSYNC_OFF_TIME (TIMER_HSYNC_PERIOD * TOP_BORDER + TIMER_HSYNC_ON_TIME) |
| r20796 | r20797 | |
| 81 | 87 | |
| 82 | 88 | #define LOG_SCANLINE 0 |
| 83 | 89 | #define LOG_HSYNC 0 |
| 84 | | #define LOG_FSYNC 0 |
| 90 | #define LOG_FSYNC 1 |
| 91 | #define LOG_FLUSH 1 |
| 92 | #define LOG_INPUT 0 |
| 85 | 93 | |
| 86 | 94 | |
| 87 | 95 | const UINT32 mc6847_base_device::s_palette[mc6847_base_device::PALETTE_LENGTH] = |
| r20796 | r20797 | |
| 117 | 125 | //------------------------------------------------- |
| 118 | 126 | |
| 119 | 127 | mc6847_friend_device::mc6847_friend_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, |
| 120 | | const UINT8 *fontdata, bool is_mc6847t1, double tpfs, int field_sync_falling_edge_scanline) |
| 128 | const UINT8 *fontdata, bool is_mc6847t1, double tpfs, int field_sync_falling_edge_scanline, bool supports_partial_body_scanlines) |
| 121 | 129 | : device_t(mconfig, type, name, tag, owner, clock), |
| 122 | | m_character_map(fontdata, is_mc6847t1) |
| 130 | m_character_map(fontdata, is_mc6847t1) |
| 123 | 131 | { |
| 124 | 132 | m_tpfs = tpfs; |
| 133 | m_supports_partial_body_scanlines = supports_partial_body_scanlines; |
| 125 | 134 | |
| 126 | 135 | // The MC6847 and the GIME apply field sync on different scanlines |
| 127 | 136 | m_field_sync_falling_edge_scanline = field_sync_falling_edge_scanline; |
| r20796 | r20797 | |
| 161 | 170 | m_top_border_scanlines = 0; |
| 162 | 171 | m_body_scanlines = 0; |
| 163 | 172 | m_wide = false; |
| 173 | m_recording_scanline = false; |
| 164 | 174 | set_geometry(25, 192, false); |
| 165 | 175 | |
| 166 | 176 | /* save states */ |
| r20796 | r20797 | |
| 295 | 305 | break; |
| 296 | 306 | |
| 297 | 307 | case SCANLINE_ZONE_BODY: |
| 298 | | record_body_scanline(m_physical_scanline, m_logical_scanline); |
| 308 | m_recording_scanline = true; |
| 309 | if (m_partial_scanline_clocks > 0) |
| 310 | record_partial_body_scanline(m_physical_scanline, m_logical_scanline, m_partial_scanline_clocks, 228); |
| 311 | else |
| 312 | record_body_scanline(m_physical_scanline, m_logical_scanline); |
| 313 | m_recording_scanline = false; |
| 299 | 314 | break; |
| 300 | 315 | |
| 301 | 316 | case SCANLINE_ZONE_RETRACE: |
| r20796 | r20797 | |
| 369 | 384 | /* advance to next scanline */ |
| 370 | 385 | m_physical_scanline++; |
| 371 | 386 | m_logical_scanline++; |
| 387 | m_partial_scanline_clocks = 0; |
| 372 | 388 | |
| 373 | 389 | /* check for movement into the next "zone" */ |
| 374 | 390 | if (m_logical_scanline_zone == SCANLINE_ZONE_FRAME_END) |
| r20796 | r20797 | |
| 447 | 463 | |
| 448 | 464 | |
| 449 | 465 | //------------------------------------------------- |
| 466 | // get_clocks_since_hsync |
| 467 | //------------------------------------------------- |
| 468 | |
| 469 | INT32 mc6847_friend_device::get_clocks_since_hsync() |
| 470 | { |
| 471 | UINT64 hsync_on_clocks = attotime_to_clocks(m_hsync_on_timer->start()); |
| 472 | UINT64 current_clocks = attotime_to_clocks(machine().time()); |
| 473 | return (INT32) (current_clocks - hsync_on_clocks); |
| 474 | } |
| 475 | |
| 476 | |
| 477 | |
| 478 | //------------------------------------------------- |
| 450 | 479 | // video_flush |
| 451 | 480 | //------------------------------------------------- |
| 452 | 481 | |
| 453 | | void mc6847_friend_device::video_flush(void) |
| 482 | void mc6847_friend_device::video_flush() |
| 454 | 483 | { |
| 484 | // first, only flush if... |
| 485 | // 1. We support partial scanlines |
| 486 | // 2. We're not already recording |
| 487 | // 3. We're in the body |
| 488 | if (m_supports_partial_body_scanlines && !m_recording_scanline && (m_logical_scanline_zone == SCANLINE_ZONE_BODY)) |
| 489 | { |
| 490 | UINT32 new_partial_scanline_clocks = get_clocks_since_hsync(); |
| 491 | if (m_partial_scanline_clocks < new_partial_scanline_clocks) |
| 492 | { |
| 493 | if (LOG_FLUSH) |
| 494 | logerror("%s: new_partial_scanline_clocks=%u\n", describe_context(), new_partial_scanline_clocks); |
| 495 | |
| 496 | m_recording_scanline = true; |
| 497 | record_partial_body_scanline(m_physical_scanline, m_logical_scanline, m_partial_scanline_clocks, new_partial_scanline_clocks); |
| 498 | m_recording_scanline = false; |
| 499 | |
| 500 | m_partial_scanline_clocks = new_partial_scanline_clocks; |
| 501 | } |
| 502 | } |
| 455 | 503 | } |
| 456 | 504 | |
| 457 | 505 | |
| r20796 | r20797 | |
| 481 | 529 | //------------------------------------------------- |
| 482 | 530 | |
| 483 | 531 | mc6847_base_device::mc6847_base_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const UINT8 *fontdata, double tpfs) |
| 484 | | : mc6847_friend_device(mconfig, type, name, tag, owner, clock, fontdata, (type == MC6847T1_NTSC) || (type == MC6847T1_PAL), tpfs, 25+191) |
| 532 | : mc6847_friend_device(mconfig, type, name, tag, owner, clock, fontdata, (type == MC6847T1_NTSC) || (type == MC6847T1_PAL), tpfs, 25+191, true) |
| 485 | 533 | { |
| 486 | 534 | m_palette = s_palette; |
| 487 | 535 | |
| r20796 | r20797 | |
| 576 | 624 | |
| 577 | 625 | |
| 578 | 626 | //------------------------------------------------- |
| 627 | // input |
| 628 | //------------------------------------------------- |
| 629 | |
| 630 | UINT8 mc6847_base_device::input(UINT16 address) |
| 631 | { |
| 632 | UINT8 data = m_res_input_func(address); |
| 633 | if (LOG_INPUT) |
| 634 | logerror("%s: input: address=0x%04X data=0x%02X\n", describe_context(), address, data); |
| 635 | return data; |
| 636 | } |
| 637 | |
| 638 | |
| 639 | |
| 640 | //------------------------------------------------- |
| 579 | 641 | // record_scanline_res |
| 580 | 642 | //------------------------------------------------- |
| 581 | 643 | |
| 582 | 644 | template<int sample_count, int yres> |
| 583 | | void mc6847_base_device::record_scanline_res(int scanline) |
| 645 | void mc6847_base_device::record_scanline_res(int scanline, INT32 start_pos, INT32 end_pos) |
| 584 | 646 | { |
| 585 | | int i, column; |
| 586 | | UINT8 data; |
| 647 | UINT8 current_sample_count = (start_pos > 0) ? m_data[scanline].m_sample_count : 0; |
| 587 | 648 | |
| 588 | | /* calculate offset */ |
| 589 | | offs_t offset = scanline / (192 / yres) * sample_count; |
| 590 | | |
| 591 | | /* main loop */ |
| 592 | | for (column = 0; column < sample_count; column++) |
| 649 | // main loop |
| 650 | for (INT32 pos = start_pos; pos < end_pos; pos++) |
| 593 | 651 | { |
| 594 | | /* input data */ |
| 595 | | data = m_res_input_func(offset++); |
| 652 | // set address at beginning of line |
| 653 | if (pos == 0) |
| 654 | m_video_address = scanline / (192 / yres) * sample_count; |
| 596 | 655 | |
| 597 | | /* update values */ |
| 598 | | update_value(&m_data[scanline].m_mode[column], simplify_mode(data, m_mode)); |
| 599 | | update_value(&m_data[scanline].m_data[column], data); |
| 600 | | } |
| 656 | if ((sample_count == 32) || ((pos % 1) == 0)) |
| 657 | { |
| 658 | // input data |
| 659 | UINT8 data = input(m_video_address++); |
| 601 | 660 | |
| 602 | | /* several more inputs occur after hblank */ |
| 603 | | for (i = 0; i < sample_count * 5 / 16; i++) |
| 604 | | { |
| 605 | | data = m_res_input_func(offset++); |
| 661 | if (pos < 32) |
| 662 | { |
| 663 | // update values |
| 664 | assert(current_sample_count >= 0); |
| 665 | assert(current_sample_count < ARRAY_LENGTH(m_data[scanline].m_mode)); |
| 666 | update_value(&m_data[scanline].m_mode[current_sample_count], simplify_mode(data, m_mode)); |
| 667 | update_value(&m_data[scanline].m_data[current_sample_count], data); |
| 668 | current_sample_count++; |
| 669 | } |
| 670 | } |
| 606 | 671 | } |
| 607 | 672 | |
| 608 | | /* update sample count */ |
| 609 | | update_value(&m_data[scanline].m_sample_count, (UINT8) sample_count); |
| 673 | // update sample count |
| 674 | update_value(&m_data[scanline].m_sample_count, current_sample_count); |
| 610 | 675 | } |
| 611 | 676 | |
| 612 | 677 | |
| r20796 | r20797 | |
| 615 | 680 | // record_body_scanline |
| 616 | 681 | //------------------------------------------------- |
| 617 | 682 | |
| 618 | | void mc6847_base_device::record_body_scanline(UINT16 physical_scanline, UINT16 scanline) |
| 683 | ATTR_FORCE_INLINE void mc6847_base_device::record_body_scanline(UINT16 physical_scanline, UINT16 scanline, INT32 start_pos, INT32 end_pos) |
| 619 | 684 | { |
| 620 | 685 | // sanity checks |
| 621 | 686 | assert(scanline < 192); |
| r20796 | r20797 | |
| 626 | 691 | { |
| 627 | 692 | case 0: |
| 628 | 693 | case MODE_GM0: |
| 629 | | record_scanline_res<16, 64>(scanline); |
| 694 | record_scanline_res<16, 64>(scanline, start_pos, end_pos); |
| 630 | 695 | break; |
| 631 | 696 | |
| 632 | 697 | case MODE_GM1: |
| 633 | | record_scanline_res<32, 64>(scanline); |
| 698 | record_scanline_res<32, 64>(scanline, start_pos, end_pos); |
| 634 | 699 | break; |
| 635 | 700 | |
| 636 | 701 | case MODE_GM1|MODE_GM0: |
| 637 | | record_scanline_res<16, 96>(scanline); |
| 702 | record_scanline_res<16, 96>(scanline, start_pos, end_pos); |
| 638 | 703 | break; |
| 639 | 704 | |
| 640 | 705 | case MODE_GM2: |
| 641 | | record_scanline_res<32, 96>(scanline); |
| 706 | record_scanline_res<32, 96>(scanline, start_pos, end_pos); |
| 642 | 707 | break; |
| 643 | 708 | |
| 644 | 709 | case MODE_GM2|MODE_GM0: |
| 645 | | record_scanline_res<16, 192>(scanline); |
| 710 | record_scanline_res<16, 192>(scanline, start_pos, end_pos); |
| 646 | 711 | break; |
| 647 | 712 | |
| 648 | 713 | case MODE_GM2|MODE_GM1: |
| 649 | 714 | case MODE_GM2|MODE_GM1|MODE_GM0: |
| 650 | | record_scanline_res<32, 192>(scanline); |
| 715 | record_scanline_res<32, 192>(scanline, start_pos, end_pos); |
| 651 | 716 | break; |
| 652 | 717 | |
| 653 | 718 | default: |
| r20796 | r20797 | |
| 658 | 723 | } |
| 659 | 724 | else |
| 660 | 725 | { |
| 661 | | record_scanline_res<32, 16>(scanline); |
| 726 | record_scanline_res<32, 16>(scanline, start_pos, end_pos); |
| 662 | 727 | } |
| 663 | 728 | } |
| 664 | 729 | |
| 665 | 730 | |
| 666 | 731 | |
| 667 | 732 | //------------------------------------------------- |
| 733 | // record_body_scanline |
| 734 | //------------------------------------------------- |
| 735 | |
| 736 | void mc6847_base_device::record_body_scanline(UINT16 physical_scanline, UINT16 scanline) |
| 737 | { |
| 738 | record_body_scanline(physical_scanline, scanline, 0, 32); |
| 739 | } |
| 740 | |
| 741 | |
| 742 | |
| 743 | //------------------------------------------------- |
| 744 | // record_partial_body_scanline |
| 745 | //------------------------------------------------- |
| 746 | |
| 747 | void mc6847_base_device::record_partial_body_scanline(UINT16 physical_scanline, UINT16 scanline, INT32 start_clock, INT32 end_clock) |
| 748 | { |
| 749 | INT32 start_pos = MAX(scanline_position_from_clock(start_clock), 0); |
| 750 | INT32 end_pos = MIN(scanline_position_from_clock(end_clock), 42); |
| 751 | |
| 752 | if (start_pos < end_pos) |
| 753 | record_body_scanline(physical_scanline, scanline, start_pos, end_pos); |
| 754 | } |
| 755 | |
| 756 | |
| 757 | |
| 758 | //------------------------------------------------- |
| 759 | // scanline_position_from_clock |
| 760 | //------------------------------------------------- |
| 761 | |
| 762 | INT32 mc6847_base_device::scanline_position_from_clock(INT32 clocks_since_hsync) |
| 763 | { |
| 764 | return (clocks_since_hsync - 20) / 4; |
| 765 | } |
| 766 | |
| 767 | |
| 768 | |
| 769 | //------------------------------------------------- |
| 668 | 770 | // field_sync_changed |
| 669 | 771 | //------------------------------------------------- |
| 670 | 772 | |
| r20796 | r20797 | |
| 1564 | 1666 | //------------------------------------------------- |
| 1565 | 1667 | |
| 1566 | 1668 | mc6847_ntsc_device::mc6847_ntsc_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) |
| 1567 | | : mc6847_base_device(mconfig, MC6847_NTSC, "MC6847_NTSC", tag, owner, clock, ntsc_square_fontdata8x12, 227.0) |
| 1669 | : mc6847_base_device(mconfig, MC6847_NTSC, "MC6847_NTSC", tag, owner, clock, ntsc_square_fontdata8x12, 262.0) |
| 1568 | 1670 | { |
| 1569 | 1671 | } |
| 1570 | 1672 | |
| r20796 | r20797 | |
| 1575 | 1677 | //------------------------------------------------- |
| 1576 | 1678 | |
| 1577 | 1679 | mc6847_pal_device::mc6847_pal_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) |
| 1578 | | : mc6847_base_device(mconfig, MC6847_PAL, "MC6847_PAL", tag, owner, clock, pal_square_fontdata8x12, 227.0) |
| 1680 | : mc6847_base_device(mconfig, MC6847_PAL, "MC6847_PAL", tag, owner, clock, pal_square_fontdata8x12, 262.0) |
| 1579 | 1681 | { |
| 1580 | 1682 | } |
| 1581 | 1683 | |
| r20796 | r20797 | |
| 1586 | 1688 | //------------------------------------------------- |
| 1587 | 1689 | |
| 1588 | 1690 | mc6847y_ntsc_device::mc6847y_ntsc_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) |
| 1589 | | : mc6847_base_device(mconfig, MC6847Y_NTSC, "MC6847Y_NTSC", tag, owner, clock, ntsc_square_fontdata8x12, 227.5) |
| 1691 | : mc6847_base_device(mconfig, MC6847Y_NTSC, "MC6847Y_NTSC", tag, owner, clock, ntsc_square_fontdata8x12, 262.5) |
| 1590 | 1692 | { |
| 1591 | 1693 | } |
| 1592 | 1694 | |
| r20796 | r20797 | |
| 1597 | 1699 | //------------------------------------------------- |
| 1598 | 1700 | |
| 1599 | 1701 | mc6847y_pal_device::mc6847y_pal_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) |
| 1600 | | : mc6847_base_device(mconfig, MC6847Y_PAL, "MC6847Y_PAL", tag, owner, clock, pal_square_fontdata8x12, 227.5) |
| 1702 | : mc6847_base_device(mconfig, MC6847Y_PAL, "MC6847Y_PAL", tag, owner, clock, pal_square_fontdata8x12, 262.5) |
| 1601 | 1703 | { |
| 1602 | 1704 | } |
| 1603 | 1705 | |
| r20796 | r20797 | |
| 1608 | 1710 | //------------------------------------------------- |
| 1609 | 1711 | |
| 1610 | 1712 | mc6847t1_ntsc_device::mc6847t1_ntsc_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) |
| 1611 | | : mc6847_base_device(mconfig, MC6847T1_NTSC, "MC6847T1_NTSC", tag, owner, clock, ntsc_round_fontdata8x12, 227.0) |
| 1713 | : mc6847_base_device(mconfig, MC6847T1_NTSC, "MC6847T1_NTSC", tag, owner, clock, ntsc_round_fontdata8x12, 262.0) |
| 1612 | 1714 | { |
| 1613 | 1715 | } |
| 1614 | 1716 | |
| r20796 | r20797 | |
| 1619 | 1721 | //------------------------------------------------- |
| 1620 | 1722 | |
| 1621 | 1723 | mc6847t1_pal_device::mc6847t1_pal_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) |
| 1622 | | : mc6847_base_device(mconfig, MC6847T1_PAL, "MC6847T1_PAL", tag, owner, clock, pal_round_fontdata8x12, 227.0) |
| 1724 | : mc6847_base_device(mconfig, MC6847T1_PAL, "MC6847T1_PAL", tag, owner, clock, pal_round_fontdata8x12, 262.0) |
| 1623 | 1725 | { |
| 1624 | 1726 | } |