From f2b3d068454e9383d887ea7d7ba472728a4dd97c Mon Sep 17 00:00:00 2001 From: Vas Crabb Date: Mon, 20 Jun 2022 09:18:12 +1000 Subject: bus/nubus: Better SuperMac Spectrum/8 CRTC emulation. * Implemented sync pulse width registers and moved sync to be logically at the end of the line/frame. * Fixed vertical offset in 480i mode and made vertical blanking interrupt fire at field frequency. --- src/devices/bus/nubus/nubus_spec8.cpp | 112 +++++++++++++++++++++++----------- src/devices/bus/nubus/nubus_spec8.h | 4 +- 2 files changed, 77 insertions(+), 39 deletions(-) diff --git a/src/devices/bus/nubus/nubus_spec8.cpp b/src/devices/bus/nubus/nubus_spec8.cpp index 318b03f6814..3f0a44be3c4 100644 --- a/src/devices/bus/nubus/nubus_spec8.cpp +++ b/src/devices/bus/nubus/nubus_spec8.cpp @@ -1,14 +1,28 @@ // license:BSD-3-Clause -// copyright-holders:R. Belmont +// copyright-holders:R. Belmont, Vas Crabb /*************************************************************************** SuperMac Spectrum/8 Series III video card - There is no sign of acceleration or blitting in any mode, and the acceleration - code from the Spectrum PDQ ROM is absent on this one. + There is no sign of acceleration or blitting in any mode, and the + acceleration code from the Spectrum PDQ ROM is absent on this one. - On first boot / with clean PRAM, press SPACE repeatedly when it shows the frame - that fills the entire screen. If you get it wrong, delete PRAM and try again. + On first boot or with clean PRAM, the firmware will cycle through video + modes and prompt you to press space when the desired mode is active (on + the monitors available at the time, only one mode would produce a stable + image). If you want to change video later, hold Option as soon as the + machine starts. + + The CRTC has 16-bit registers with the bytes written at separate + addresses offset by 2. The most significant byte is at the higher + address. + + The video timing control registers are counter preload values, so they + effectively function as (65'546 - x) to get the actual number of pixels + cells or lines. They're represented as signed 16-bit integers as that + lets you see the negated value. The horizontal timing register values + are in units of four pixels. The sync pulses are treated as being at + the start of the line/frame in the configuration registers. ***************************************************************************/ @@ -122,9 +136,11 @@ void nubus_spec8s3_device::device_start() save_item(NAME(m_colors)); save_item(NAME(m_count)); save_item(NAME(m_clutoffs)); + save_item(NAME(m_hsync)); save_item(NAME(m_hstart)); save_item(NAME(m_hend)); save_item(NAME(m_htotal)); + save_item(NAME(m_vsync)); save_item(NAME(m_vstart)); save_item(NAME(m_vend)); save_item(NAME(m_vtotal)); @@ -146,9 +162,11 @@ void nubus_spec8s3_device::device_reset() std::fill(std::begin(m_colors), std::end(m_colors), 0); m_count = 0; m_clutoffs = 0; + m_hsync = -24; m_hstart = -64; m_hend = -320; m_htotal = -332; + m_vsync = -3; m_vstart = -33; m_vend = -801; m_vtotal = -804; @@ -171,7 +189,7 @@ TIMER_CALLBACK_MEMBER(nubus_spec8s3_device::vbl_tick) m_vbl_pending = true; } - m_timer->adjust(screen().time_until_pos(-m_vend * (m_interlace ? 2 : 1), 0)); + m_timer->adjust(screen().time_until_pos((m_vsync - m_vend) * (m_interlace ? 2 : 1), 0)); } /*************************************************************************** @@ -209,21 +227,25 @@ void nubus_spec8s3_device::update_crtc() } // for some reason you temporarily get invalid screen parameters - ignore them - if ((m_hend < m_hstart) && (m_htotal < m_hend) && (m_vend < m_vstart) && (m_vtotal < m_vend)) + if ((m_hstart < m_hsync) && (m_hend < m_hstart) && (m_htotal < m_hend) && (m_vstart < m_vsync) && (m_vend < m_vstart) && (m_vtotal < m_vend)) { screen().configure( -4 * m_htotal, -m_vtotal * (m_interlace ? 2 : 1), rectangle( - -4 * m_hstart, (-4 * m_hend) - 1, - -m_vstart * (m_interlace ? 2 : 1), (-m_vend * (m_interlace ? 2 : 1)) - 1), - attotime::from_ticks(-4 * m_htotal * -m_vtotal * (m_interlace ? 2 : 1), clock).attoseconds()); + 4 * (m_hsync - m_hstart), + (4 * (m_hsync - m_hend)) - 1, + (m_vsync - m_vstart) * (m_interlace ? 2 : 1), + ((m_vsync - m_vend) * (m_interlace ? 2 : 1)) - 1), + attotime::from_ticks(-4 * m_htotal * -m_vtotal, clock).attoseconds()); - m_timer->adjust(screen().time_until_pos(-m_vend * (m_interlace ? 2 : 1), 0)); + m_timer->adjust(screen().time_until_pos((m_vsync - m_vend) * (m_interlace ? 2 : 1), 0)); } else { - LOG("Ignoring invalid CRTC parameters (%d %d %d) (%d %d %d)\n", m_hstart, m_hend, m_htotal, m_vstart, m_vend, m_vtotal); + LOG("Ignoring invalid CRTC parameters (%d %d %d %d) (%d %d %d %d)\n", + m_hsync, m_hstart, m_hend, m_htotal, + m_vsync, m_vstart, m_vend, m_vtotal); } } @@ -231,22 +253,24 @@ uint32_t nubus_spec8s3_device::screen_update(screen_device &screen, bitmap_rgb32 { auto const vram8 = util::big_endian_cast(&m_vram[0]) + 0x400; - const int vstart = -m_vstart * (m_interlace ? 2 : 1); - const int vend = -m_vend * (m_interlace ? 2 : 1); + int const hstart = 4 * (m_hsync - m_hstart); + int const width = 4 * (m_hstart - m_hend); + int const vstart = (m_vsync - m_vstart) * (m_interlace ? 2 : 1); + int const vend = (m_vsync - m_vend) * (m_interlace ? 2 : 1); switch (m_mode) { case 0: // 1 bpp for (int y = cliprect.top(); y <= cliprect.bottom(); y++) { - uint32_t *scanline = &bitmap.pix(y, -4 * m_hstart); + uint32_t *scanline = &bitmap.pix(y, hstart); if ((y >= vstart) && (y < vend)) { - for (int x = 0; x < ((m_hstart - m_hend) / 2); x++) + for (int x = 0; x < (width / 8); x++) { - uint8_t const pixels = vram8[((y + m_vstart) * 512) + x]; + uint8_t const pixels = vram8[((y - vstart) * 512) + x]; - *scanline++ = m_palette[pixels & 0x80]; + *scanline++ = m_palette[(pixels << 0) & 0x80]; *scanline++ = m_palette[(pixels << 1) & 0x80]; *scanline++ = m_palette[(pixels << 2) & 0x80]; *scanline++ = m_palette[(pixels << 3) & 0x80]; @@ -258,7 +282,7 @@ uint32_t nubus_spec8s3_device::screen_update(screen_device &screen, bitmap_rgb32 } else { - std::fill_n(scanline, (m_hstart - m_hend) * 4, 0); + std::fill_n(scanline, width, 0); } } break; @@ -266,22 +290,22 @@ uint32_t nubus_spec8s3_device::screen_update(screen_device &screen, bitmap_rgb32 case 1: // 2 bpp for (int y = cliprect.top(); y <= cliprect.bottom(); y++) { - uint32_t *scanline = &bitmap.pix(y, -4 * m_hstart); + uint32_t *scanline = &bitmap.pix(y, hstart); if ((y >= vstart) && (y < vend)) { - for (int x = 0; x < (m_hstart - m_hend); x++) + for (int x = 0; x < (width / 4); x++) { - uint8_t const pixels = vram8[((y + m_vstart) * 512) + x]; + uint8_t const pixels = vram8[((y - vstart) * 512) + x]; - *scanline++ = m_palette[pixels&0xc0]; - *scanline++ = m_palette[(pixels<<2)&0xc0]; - *scanline++ = m_palette[(pixels<<4)&0xc0]; - *scanline++ = m_palette[(pixels<<6)&0xc0]; + *scanline++ = m_palette[(pixels << 0) & 0xc0]; + *scanline++ = m_palette[(pixels << 2) & 0xc0]; + *scanline++ = m_palette[(pixels << 4) & 0xc0]; + *scanline++ = m_palette[(pixels << 6) & 0xc0]; } } else { - std::fill_n(scanline, (m_hstart - m_hend) * 4, 0); + std::fill_n(scanline, width, 0); } } break; @@ -289,20 +313,20 @@ uint32_t nubus_spec8s3_device::screen_update(screen_device &screen, bitmap_rgb32 case 2: // 4 bpp for (int y = cliprect.top(); y <= cliprect.bottom(); y++) { - uint32_t *scanline = &bitmap.pix(y, -4 * m_hstart); + uint32_t *scanline = &bitmap.pix(y, hstart); if ((y >= vstart) && (y < vend)) { - for (int x = 0; x < ((m_hstart - m_hend) * 2); x++) + for (int x = 0; x < (width / 2); x++) { - uint8_t const pixels = vram8[((y + m_vstart) * 512) + x]; + uint8_t const pixels = vram8[((y - vstart) * 512) + x]; - *scanline++ = m_palette[pixels&0xf0]; - *scanline++ = m_palette[(pixels<<4)&0xf0]; + *scanline++ = m_palette[(pixels << 0) & 0xf0]; + *scanline++ = m_palette[(pixels << 4) & 0xf0]; } } else { - std::fill_n(scanline, (m_hstart - m_hend) * 4, 0); + std::fill_n(scanline, width, 0); } } break; @@ -310,18 +334,18 @@ uint32_t nubus_spec8s3_device::screen_update(screen_device &screen, bitmap_rgb32 case 3: // 8 bpp for (int y = cliprect.top(); y <= cliprect.bottom(); y++) { - uint32_t *scanline = &bitmap.pix(y, -4 * m_hstart); + uint32_t *scanline = &bitmap.pix(y, hstart); if ((y >= vstart) && (y < vend)) { - for (int x = 0; x < ((m_hstart - m_hend) * 4); x++) + for (int x = 0; x < width; x++) { - uint8_t const pixels = vram8[((y + m_vstart) * 1024) + x]; + uint8_t const pixels = vram8[((y - vstart) * 1024) + x]; *scanline++ = m_palette[pixels]; } } else { - std::fill_n(scanline, (m_hstart - m_hend) * 4, 0); + std::fill_n(scanline, width, 0); } } break; @@ -336,6 +360,13 @@ void nubus_spec8s3_device::spec8s3_w(offs_t offset, uint32_t data, uint32_t mem_ { switch (offset) { + case 0x3804: + case 0x3806: + crtc_w(m_hsync, offset, data); + if (!BIT(offset, 1)) + update_crtc(); + break; + case 0x3808: case 0x380a: crtc_w(m_hstart, offset, data); @@ -357,6 +388,13 @@ void nubus_spec8s3_device::spec8s3_w(offs_t offset, uint32_t data, uint32_t mem_ update_crtc(); break; + case 0x3818: + case 0x381a: + crtc_w(m_vsync, offset, data); + if (!BIT(offset, 1)) + update_crtc(); + break; + case 0x381c: case 0x381e: crtc_w(m_vstart, offset, data); diff --git a/src/devices/bus/nubus/nubus_spec8.h b/src/devices/bus/nubus/nubus_spec8.h index c917de93e7f..c928022b52e 100644 --- a/src/devices/bus/nubus/nubus_spec8.h +++ b/src/devices/bus/nubus/nubus_spec8.h @@ -53,8 +53,8 @@ private: uint32_t m_mode, m_vbl_disable; uint32_t m_palette[256], m_colors[3], m_count, m_clutoffs; - int16_t m_hstart, m_hend, m_htotal; - int16_t m_vstart, m_vend, m_vtotal; + int16_t m_hsync, m_hstart, m_hend, m_htotal; + int16_t m_vsync, m_vstart, m_vend, m_vtotal; bool m_interlace; bool m_vbl_pending; -- cgit v1.2.3