summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices
diff options
context:
space:
mode:
author Vas Crabb <vas@vastheman.com>2022-06-20 09:18:12 +1000
committer Vas Crabb <vas@vastheman.com>2022-06-20 09:18:12 +1000
commitf2b3d068454e9383d887ea7d7ba472728a4dd97c (patch)
tree3d77cd57c045cb67051413704c73138813f23abf /src/devices
parent742de90a6537d2fa6c539cc674e8b3176fdaf9e2 (diff)
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.
Diffstat (limited to 'src/devices')
-rw-r--r--src/devices/bus/nubus/nubus_spec8.cpp112
-rw-r--r--src/devices/bus/nubus/nubus_spec8.h4
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<uint8_t const>(&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;