From 98978eacf37c77f193e7d714339dcf9c7df1ee3a Mon Sep 17 00:00:00 2001 From: AJR Date: Sat, 9 Feb 2019 17:49:28 -0500 Subject: [PATCH] scn2674: Row buffering and timing improvements - Add optional read callbacks for row buffering DMA - Add MBC output - Correct timing of BREQ and VBLANK outputs - Improve character blink and cursor blink timings wy50: Power-up tests pass now; very preliminary character display (nw) --- src/devices/video/scn2674.cpp | 339 ++++++++++++++++++++-------------- src/devices/video/scn2674.h | 18 +- src/mame/drivers/wy50.cpp | 58 +++++- 3 files changed, 269 insertions(+), 146 deletions(-) diff --git a/src/devices/video/scn2674.cpp b/src/devices/video/scn2674.cpp index 9baae770f4c..8957171848f 100644 --- a/src/devices/video/scn2674.cpp +++ b/src/devices/video/scn2674.cpp @@ -44,12 +44,15 @@ scn2674_device::scn2674_device(const machine_config &mconfig, device_type type, , device_memory_interface(mconfig, *this) , m_intr_cb(*this) , m_breq_cb(*this) + , m_mbc_cb(*this) + , m_mbc_char_cb(*this) + , m_mbc_attr_cb(*this) , m_IR_pointer(0) , m_screen1_address(0), m_screen2_address(0) , m_cursor_address(0) , m_irq_register(0), m_status_register(0), m_irq_mask(0) , m_gfx_enabled(false) - , m_display_enabled(false), m_display_enabled_field(false), m_display_enabled_scanline(false) + , m_display_enabled(false), m_dadd_enabled(false), m_display_enabled_field(false), m_display_enabled_scanline(false) , m_cursor_enabled(false) , m_hpixels_per_column(0) , m_double_ht_wd(false) @@ -83,6 +86,8 @@ scn2674_device::scn2674_device(const machine_config &mconfig, device_type type, , m_char_buffer(0), m_attr_buffer(0) , m_linecounter(0), m_address(0), m_start1change(0) , m_scanline_timer(nullptr) + , m_breq_timer(nullptr) + , m_vblank_timer(nullptr) , m_char_space(nullptr), m_attr_space(nullptr) , m_char_space_config("charram", ENDIANNESS_LITTLE, 8, extend_addressing ? 16 : 14, 0, address_map_constructor(), address_map_constructor(FUNC(scn2674_device::scn2674_vram), this)) , m_attr_space_config("attrram", ENDIANNESS_LITTLE, 8, extend_addressing ? 16 : 14, 0, address_map_constructor(), address_map_constructor(FUNC(scn2674_device::scn2674_vram), this)) @@ -105,7 +110,12 @@ void scn2674_device::device_start() m_display_cb.bind_relative_to(*owner()); m_intr_cb.resolve_safe(); m_breq_cb.resolve_safe(); - m_scanline_timer = timer_alloc(TIMER_SCANLINE); + m_mbc_cb.resolve_safe(); + m_mbc_char_cb.resolve(); + m_mbc_attr_cb.resolve(); + m_scanline_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(scn2674_device::scanline_timer), this)); + m_breq_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(scn2674_device::breq_timer), this)); + m_vblank_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(scn2674_device::vblank_timer), this)); screen().register_screen_bitmap(m_bitmap); m_char_space = &space(0); @@ -123,6 +133,7 @@ void scn2674_device::device_start() save_item(NAME(m_irq_mask)); save_item(NAME(m_gfx_enabled)); save_item(NAME(m_display_enabled)); + save_item(NAME(m_dadd_enabled)); save_item(NAME(m_display_enabled_field)); save_item(NAME(m_display_enabled_scanline)); save_item(NAME(m_cursor_enabled)); @@ -174,6 +185,7 @@ void scn2674_device::device_reset() m_irq_mask = 0; m_gfx_enabled = false; m_display_enabled = false; + m_dadd_enabled = false; m_display_enabled_field = false; m_display_enabled_scanline = false; m_cursor_enabled = false; @@ -551,9 +563,15 @@ void scn2674_device::set_display_enabled(bool enabled, int n) m_display_enabled = false; if (n == 1) + { LOGMASKED(LOG_COMMAND, "%s: Display off - float DADD bus\n", machine().describe_context()); + m_dadd_enabled = false; + } else + { LOGMASKED(LOG_COMMAND, "%s: Display off - no float DADD bus\n", machine().describe_context()); + m_dadd_enabled = true; + } } else { @@ -766,6 +784,7 @@ void scn2674_device::write_command(uint8_t data) m_irq_mask = 0x00; m_gfx_enabled = false; m_display_enabled = false; + m_dadd_enabled = false; m_cursor_enabled = false; m_use_row_table = false; m_intr_cb(CLEAR_LINE); @@ -911,6 +930,8 @@ void scn2674_device::write(offs_t offset, uint8_t data) m_screen1_address = (m_screen1_address & 0x3f00) | data; if (!screen().vblank()) m_start1change = (m_linecounter / m_scanline_per_char_row) + 1; + else + m_start1change = 0; break; case 3: @@ -924,6 +945,8 @@ void scn2674_device::write(offs_t offset, uint8_t data) } if (!screen().vblank()) m_start1change = (m_linecounter / m_scanline_per_char_row) + 1; + else + m_start1change = 0; break; case 4: @@ -967,145 +990,191 @@ void scn2674_device::recompute_parameters() rectangle visarea(0, max_visible_x, 0, max_visible_y); screen().configure(horiz_pix_total, vert_pix_total, visarea, refresh.as_attoseconds()); - m_scanline_timer->adjust(screen().time_until_pos(0, 0), 0, screen().scan_period()); + m_linecounter = screen().vpos(); + m_scanline_timer->adjust(screen().time_until_pos((m_linecounter + 1) % vert_pix_total, 0), 0, screen().scan_period()); } -void scn2674_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +TIMER_CALLBACK_MEMBER(scn2674_device::scanline_timer) { - switch (id) + int dw = m_double_ht_wd ? m_double[0] : 0; // double width + if (((m_display_enabled_scanline) || (m_display_enabled_field && !m_interlace_enable)) && (!m_display_enabled)) { - case TIMER_SCANLINE: + m_display_enabled = true; + m_dadd_enabled = true; + m_display_enabled_scanline = false; + m_display_enabled_field = false; + } + + m_linecounter++; + + if (m_linecounter >= screen().height()) + { + m_linecounter = 0; + m_address = m_screen1_address; + } + + bool lastscan = m_linecounter == (m_rows_per_screen * m_scanline_per_char_row) - 1; + if (lastscan) + { + int horz_sync_begin = 2 * (m_equalizing_constant + 2 * m_horz_sync_width) - m_horz_sync_width - m_horz_back_porch; + m_vblank_timer->adjust(clocks_to_attotime(horz_sync_begin)); + } + + if (m_linecounter >= (m_rows_per_screen * m_scanline_per_char_row)) + { + if (m_buffer_mode_select == 3 && m_linecounter == (screen().height() - 1)) + m_breq_timer->adjust(clocks_to_attotime(m_character_per_row + 1), ASSERT_LINE); + return; + } + + int charrow = m_linecounter % m_scanline_per_char_row; + int tilerow = charrow; + + // should be triggered at the start of each ROW (line zero for that row) + if (charrow == 0) + { + m_status_register |= 0x08; + if (BIT(m_irq_mask, 3)) { - int dw = m_double_ht_wd ? m_double[0] : 0; // double width - if (((m_display_enabled_scanline) || (m_display_enabled_field && !m_interlace_enable)) && (!m_display_enabled)) - { - m_display_enabled = true; - m_display_enabled_scanline = false; - m_display_enabled_field = false; - } - - m_linecounter++; - - if (m_linecounter >= screen().height()) - { - m_linecounter = 0; - m_address = m_screen1_address; - } - - if (m_linecounter == (m_rows_per_screen * m_scanline_per_char_row)) - { - m_status_register |= 0x10; - if (BIT(m_irq_mask, 4)) - { - LOGMASKED(LOG_INTR, "V-Blank interrupt at line %d\n", m_linecounter); - m_irq_register |= 0x10; - m_intr_cb(ASSERT_LINE); - } - } - - if (m_linecounter >= (m_rows_per_screen * m_scanline_per_char_row)) - break; - - int charrow = m_linecounter % m_scanline_per_char_row; - int tilerow = charrow; - - // should be triggered at the start of each ROW (line zero for that row) - if (charrow == 0) - { - m_status_register |= 0x08; - if (BIT(m_irq_mask, 3)) - { - LOGMASKED(LOG_INTR, "Line Zero interrupt at line %d\n", m_linecounter); - m_irq_register |= 0x08; - m_intr_cb(ASSERT_LINE); - } - if (m_buffer_mode_select == 3) - m_breq_cb(ASSERT_LINE); - } - else if (m_buffer_mode_select == 3) - m_breq_cb(CLEAR_LINE); - - // Handle screen splits - for (int s = 0; s < 2; s++) - { - if ((m_linecounter == ((m_split_register[s] + 1) * m_scanline_per_char_row)) && m_linecounter) - { - uint8_t flag = (s == 0) ? 0x04 : 0x01; - m_status_register |= flag; - if ((m_irq_mask & flag) != 0) - { - LOGMASKED(LOG_INTR, "Split Screen %d interrupt at line %d\n", s + 1, m_linecounter); - m_irq_register |= flag; - m_intr_cb(ASSERT_LINE); - } - if (m_spl[s]) - m_address = m_screen2_address; - if (!m_double_ht_wd) - dw = m_double[s]; - } - } - - if (!m_display_enabled) - break; - - if (m_use_row_table) - { - if (m_double_ht_wd) - dw = m_screen1_address >> 14; - if (!charrow) - { - uint16_t addr = m_screen2_address; - uint16_t line = m_char_space->read_word(addr); - m_screen1_address = line; - if (m_double_ht_wd) - { - dw = line >> 14; - line &= ~0xc000; - } - m_address = line; - addr += 2; - m_screen2_address = addr & 0x3fff; - } - } - else if (m_start1change && (m_start1change == (m_linecounter / m_scanline_per_char_row))) - { - m_address = m_screen1_address; - m_start1change = 0; - } - - if (dw == 2) - tilerow >>= 1; - else if (dw == 3) - tilerow = (charrow + m_scanline_per_char_row) >> 1; - - uint16_t address = m_address; - - for (int i = 0; i < m_character_per_row; i++) - { - bool cursor_on = ((address & 0x3fff) == m_cursor_address); - - if (!m_display_cb.isnull()) - m_display_cb(m_bitmap, - i * m_hpixels_per_column, - m_linecounter, - tilerow, - m_char_space->read_byte(address), - m_attr_space != nullptr ? m_attr_space->read_byte(address) : 0, - address, - (charrow >= m_cursor_first_scanline) && (charrow <= m_cursor_last_scanline) && cursor_on, - dw != 0, - m_gfx_enabled, - charrow == m_cursor_underline_position, - m_cursor_blink && (screen().frame_number() & m_cursor_rate_divisor)); - address = (address + 1) & 0xffff; - - if (address > ((m_display_buffer_last_address << 10) | 0x3ff)) - address = m_display_buffer_first_address; - } - - if (m_gfx_enabled || (charrow == (m_scanline_per_char_row - 1))) - m_address = address; + LOGMASKED(LOG_INTR, "Line Zero interrupt at line %d\n", m_linecounter); + m_irq_register |= 0x08; + m_intr_cb(ASSERT_LINE); } + if (m_buffer_mode_select == 3) + { + m_mbc_cb(1); + m_breq_timer->adjust(clocks_to_attotime(m_character_per_row + 1), CLEAR_LINE); + } + } + else if (m_buffer_mode_select == 3 && charrow == (m_scanline_per_char_row - 1) && !lastscan) + m_breq_timer->adjust(clocks_to_attotime(m_character_per_row + 1), ASSERT_LINE); + + // Handle screen splits + for (int s = 0; s < 2; s++) + { + if ((m_linecounter == ((m_split_register[s] + 1) * m_scanline_per_char_row)) && m_linecounter) + { + uint8_t flag = (s == 0) ? 0x04 : 0x01; + m_status_register |= flag; + if ((m_irq_mask & flag) != 0) + { + LOGMASKED(LOG_INTR, "Split Screen %d interrupt at line %d\n", s + 1, m_linecounter); + m_irq_register |= flag; + m_intr_cb(ASSERT_LINE); + } + if (m_spl[s]) + m_address = m_screen2_address; + if (!m_double_ht_wd) + dw = m_double[s]; + } + } + + // WY-50 requires that normal row buffering take place even after a "display off" command + if (!m_dadd_enabled) + return; + + if (m_use_row_table) + { + if (m_double_ht_wd) + dw = m_screen1_address >> 14; + if (!charrow) + { + uint16_t addr = m_screen2_address; + uint16_t line = m_char_space->read_word(addr); + m_screen1_address = line; + if (m_double_ht_wd) + { + dw = line >> 14; + line &= ~0xc000; + } + m_address = line; + addr += 2; + m_screen2_address = addr & 0x3fff; + } + } + else if (m_start1change && (m_start1change == (m_linecounter / m_scanline_per_char_row))) + { + m_address = m_screen1_address; + m_start1change = 0; + } + + if (dw == 2) + tilerow >>= 1; + else if (dw == 3) + tilerow = (charrow + m_scanline_per_char_row) >> 1; + + uint16_t address = m_address; + + const bool mbc = (charrow == 0) && (m_buffer_mode_select == 3); + const bool blink_on = (screen().frame_number() & (m_character_blink_rate_divisor >> 1)) != 0; + for (int i = 0; i < m_character_per_row; i++) + { + u8 charcode, attrcode = 0; + if (mbc && !m_mbc_char_cb.isnull()) + { + // row buffering DMA + charcode = m_mbc_char_cb(address); + m_char_space->write_byte(address, charcode); + if (m_attr_space != nullptr && !m_mbc_attr_cb.isnull()) + { + attrcode = m_mbc_attr_cb(address); + m_attr_space->write_byte(address, attrcode); + } + } + else + { + charcode = m_char_space->read_byte(address); + if (m_attr_space != nullptr) + attrcode = m_attr_space->read_byte(address); + } + + if (m_display_enabled && !m_display_cb.isnull()) + { + bool cursor_on = ((address & 0x3fff) == m_cursor_address) + && m_cursor_enabled + && (charrow >= m_cursor_first_scanline) + && (charrow <= m_cursor_last_scanline) + && (!m_cursor_blink || (screen().frame_number() & (m_cursor_rate_divisor >> 1)) != 0); + m_display_cb(m_bitmap, + i * m_hpixels_per_column, + m_linecounter, + tilerow, + charcode, + attrcode, + address, + cursor_on, + dw != 0, + m_gfx_enabled, + charrow == m_cursor_underline_position, + blink_on); + + } + address = (address + 1) & 0xffff; + + if (address > ((m_display_buffer_last_address << 10) | 0x3ff)) + address = m_display_buffer_first_address; + } + + if (m_gfx_enabled || (charrow == (m_scanline_per_char_row - 1))) + m_address = address; +} + +TIMER_CALLBACK_MEMBER(scn2674_device::breq_timer) +{ + LOGMASKED(LOG_INTR, "BREQ %sasserted at line %d\n", (param == ASSERT_LINE) ? "" : "de", m_linecounter); + m_breq_cb(param); + if (param == CLEAR_LINE) + m_mbc_cb(0); +} + +TIMER_CALLBACK_MEMBER(scn2674_device::vblank_timer) +{ + m_status_register |= 0x10; + if (BIT(m_irq_mask, 4)) + { + LOGMASKED(LOG_INTR, "V-Blank interrupt at line %d\n", m_linecounter); + m_irq_register |= 0x10; + m_intr_cb(ASSERT_LINE); } } diff --git a/src/devices/video/scn2674.h b/src/devices/video/scn2674.h index 3090f740f8d..2b7367eeed0 100644 --- a/src/devices/video/scn2674.h +++ b/src/devices/video/scn2674.h @@ -23,6 +23,9 @@ public: // static configuration auto intr_callback() { return m_intr_cb.bind(); } auto breq_callback() { return m_breq_cb.bind(); } + auto mbc_callback() { return m_mbc_cb.bind(); } + auto mbc_char_callback() { return m_mbc_char_cb.bind(); } + auto mbc_attr_callback() { return m_mbc_attr_cb.bind(); } void set_character_width(int value) { m_hpixels_per_column = value; } template @@ -54,14 +57,20 @@ protected: virtual void device_start() override; virtual void device_reset() override; - virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override; virtual space_config_vector memory_space_config() const override; + TIMER_CALLBACK_MEMBER(scanline_timer); + TIMER_CALLBACK_MEMBER(breq_timer); + TIMER_CALLBACK_MEMBER(vblank_timer); + //protected: bitmap_rgb32 m_bitmap; devcb_write_line m_intr_cb; devcb_write_line m_breq_cb; + devcb_write_line m_mbc_cb; + devcb_read8 m_mbc_char_cb; + devcb_read8 m_mbc_attr_cb; uint8_t m_IR_pointer; uint16_t m_screen1_address; @@ -72,6 +81,7 @@ protected: uint8_t m_irq_mask; bool m_gfx_enabled; bool m_display_enabled; + bool m_dadd_enabled; bool m_display_enabled_field; bool m_display_enabled_scanline; bool m_cursor_enabled; @@ -129,14 +139,12 @@ protected: draw_character_delegate m_display_cb; emu_timer *m_scanline_timer; + emu_timer *m_breq_timer; + emu_timer *m_vblank_timer; address_space *m_char_space; address_space *m_attr_space; const address_space_config m_char_space_config; const address_space_config m_attr_space_config; - enum - { - TIMER_SCANLINE - }; }; class scn2672_device : public scn2674_device diff --git a/src/mame/drivers/wy50.cpp b/src/mame/drivers/wy50.cpp index 97ea61d0a19..7186ef1676c 100644 --- a/src/mame/drivers/wy50.cpp +++ b/src/mame/drivers/wy50.cpp @@ -41,6 +41,7 @@ public: , m_pvtc(*this, "pvtc") , m_sio(*this, "sio") , m_chargen(*this, "chargen") + , m_videoram(*this, "videoram%u", 0U) { } @@ -51,6 +52,7 @@ protected: virtual void machine_reset() override; private: + u8 pvtc_videoram_r(offs_t offset); SCN2672_DRAW_CHARACTER_MEMBER(draw_character); u8 pvtc_r(offs_t offset); @@ -65,6 +67,7 @@ private: void prg_map(address_map &map); void io_map(address_map &map); + void row_buffer_map(address_map &map); required_device m_maincpu; required_device m_earom; @@ -72,10 +75,19 @@ private: required_device m_sio; required_region_ptr m_chargen; + required_shared_ptr_array m_videoram; + + u8 m_row_buffer_char; + bool m_is_132; }; void wy50_state::machine_start() { + m_row_buffer_char = 0; + m_is_132 = false; + + save_item(NAME(m_row_buffer_char)); + save_item(NAME(m_is_132)); } void wy50_state::machine_reset() @@ -84,8 +96,27 @@ void wy50_state::machine_reset() earom_w(0); } +u8 wy50_state::pvtc_videoram_r(offs_t offset) +{ + m_row_buffer_char = m_videoram[BIT(offset, 13)][offset & 0x07ff]; + return m_row_buffer_char; +} + SCN2672_DRAW_CHARACTER_MEMBER(wy50_state::draw_character) { + bool is_attr = (charcode & 0xe0) == 0x80; + u16 dots = is_attr ? 0 : m_chargen[charcode << 4 | linecount] << 1; + + if (cursor) + dots = ~dots; + + for (int i = 0; i < 9; i++) + { + bitmap.pix32(y, x++) = BIT(dots, 8) ? rgb_t::white() : rgb_t::black(); + dots <<= 1; + } + if (!m_is_132) + bitmap.pix32(y, x++) = BIT(dots, 8) ? rgb_t::white() : rgb_t::black(); } u8 wy50_state::pvtc_r(offs_t offset) @@ -111,7 +142,7 @@ void wy50_state::sio_w(offs_t offset, u8 data) u8 wy50_state::rbreg_r() { // LS374 row buffer diagnostic register - return 0; + return m_row_buffer_char; } void wy50_state::keyboard_w(u8 data) @@ -132,8 +163,8 @@ u8 wy50_state::p1_r() { // P1.0 = AUX RDY // P1.1 = NVD OUT - // P1.4 = KEY (inverted) - return 0xfd | (m_earom->data_r() << 1); + // P1.4 = KEY (inverted, active high) + return 0xed | (m_earom->data_r() << 1); } void wy50_state::p1_w(u8 data) @@ -143,6 +174,13 @@ void wy50_state::p1_w(u8 data) // P1.5 = BEEPER // P1.6 = REV/DIM PROT // P1.7 (inverted) = 80/132 + + if (m_is_132 != BIT(data, 7)) + { + m_is_132 = BIT(data, 7); + m_pvtc->set_character_width(m_is_132 ? 9 : 10); + m_pvtc->set_unscaled_clock(68.85_MHz_XTAL / (m_is_132 ? 20 : 30)); + } } void wy50_state::prg_map(address_map &map) @@ -152,8 +190,8 @@ void wy50_state::prg_map(address_map &map) void wy50_state::io_map(address_map &map) { - map(0x0000, 0x07ff).mirror(0x1800).ram(); - map(0x2000, 0x27ff).mirror(0x1800).ram(); + map(0x0000, 0x07ff).mirror(0x1800).ram().share("videoram0"); + map(0x2000, 0x27ff).mirror(0x1800).ram().share("videoram1"); map(0x4000, 0x47ff).mirror(0x1800).rw(FUNC(wy50_state::pvtc_r), FUNC(wy50_state::pvtc_w)); map(0x6000, 0x63ff).mirror(0x1c00).rw(FUNC(wy50_state::sio_r), FUNC(wy50_state::sio_w)); map(0x8000, 0x8000).mirror(0x1fff).r(FUNC(wy50_state::rbreg_r)); @@ -161,6 +199,12 @@ void wy50_state::io_map(address_map &map) map(0xc000, 0xc000).mirror(0x1fff).w(FUNC(wy50_state::earom_w)); } +void wy50_state::row_buffer_map(address_map &map) +{ + map.global_mask(0x0ff); + map(0x000, 0x0ff).ram(); +} + static INPUT_PORTS_START(wy50) INPUT_PORTS_END @@ -183,12 +227,14 @@ void wy50_state::wy50(machine_config &config) SCN2672(config, m_pvtc, 68.85_MHz_XTAL / 30); // SCN2672A or SCN2672B m_pvtc->set_screen("screen"); m_pvtc->set_character_width(10); // 9 in 132-column mode + m_pvtc->set_addrmap(0, &wy50_state::row_buffer_map); m_pvtc->set_display_callback(FUNC(wy50_state::draw_character)); m_pvtc->intr_callback().set_inputline(m_maincpu, MCS51_T0_LINE); m_pvtc->breq_callback().set_inputline(m_maincpu, MCS51_INT0_LINE); + m_pvtc->mbc_char_callback().set(FUNC(wy50_state::pvtc_videoram_r)); MC2661(config, m_sio, 4.9152_MHz_XTAL); // SCN2661B - m_sio->txrdy_handler().set_inputline(m_maincpu, MCS51_INT1_LINE); + m_sio->rxrdy_handler().set_inputline(m_maincpu, MCS51_INT1_LINE); } ROM_START(wy50)