summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/bus/nubus/nubus_48gc.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/bus/nubus/nubus_48gc.cpp')
-rw-r--r--src/devices/bus/nubus/nubus_48gc.cpp1178
1 files changed, 997 insertions, 181 deletions
diff --git a/src/devices/bus/nubus/nubus_48gc.cpp b/src/devices/bus/nubus/nubus_48gc.cpp
index b9b831c6de2..9237b893150 100644
--- a/src/devices/bus/nubus/nubus_48gc.cpp
+++ b/src/devices/bus/nubus/nubus_48gc.cpp
@@ -1,25 +1,232 @@
// license:BSD-3-Clause
-// copyright-holders:R. Belmont
+// copyright-holders:R. Belmont, Vas Crabb
/***************************************************************************
- Apple 4*8 Graphics Card (model 630-0400) emulation
- Apple 8*24 Graphics Card emulation (cards have the same framebuffer chip
- w/different ROMs and RAMDACs, apparently)
+ Apple Macintosh Display Card 4•8 (MDC 1.0.1, model 630-0400)
+ Apple Macintosh Display Card 8•24 (MDC 1.2)
+
+ Cards have the same framebuffer, CRTC, clock synthesizer, and RAMDAC,
+ but use different ROMs and support different monitor profiles.
+
+ When 1 MB VRAM is installed, 24-bit direct color is available at up
+ to 640×480 resolution, 8-bit indexed color is available at all
+ supported resolutions, and 1:2:1 convolution is used for interlaced
+ modes with indexed color. When 512 kB VRAM is installed, 8-bit
+ indexed color is available at up to 640×480 resolution, 4-bit indexed
+ color is available at all supported resolutions, and 1:2:1
+ convolution will not be used.
+
+ Monitor type changes take effect on had reset. MDC 1.2 defaults to
+ the “Page-White Gamma” profile for the 21" and 16" color monitors,
+ which affects white balance. Use the Monitors control panel to switch
+ to the “Uncorrected Gamma” profile if you don’t like it.
+
+ System 6 will hang on start if 1 MB VRAM is installed, a PAL monitor
+ or encoder is connected, and the card has not been set up. To avoid
+ this, start the system with a different monitor connected, use the
+ Monitors control panel to select a color mode and resolution, and shut
+ down the system cleanly. After this, a PAL monitor or encoder can be
+ connected. System 7 does not suffer from this issue.
+
+ The CRTC counts half-lines vertically, which doesn’t integrate very
+ well with MAME’s screen device. The screen device also lacks any
+ support for interlaced modes. To make interlaced modes usable, a few
+ simplifying assumptions are made:
+ * Assume the framebuffer controller’s interlaced mode will be set when
+ when CRTC is configured for interlaced modes (and vice versa).
+ * Assume NTSC-like structure where frame starts with even field where
+ vertical sync coincides with horizontal sync.
+ * Assume the framebuffer controller and RAMDAC are configured for the
+ same pixel format.
+
+ TODO:
+ * Precise interrupt timing.
+ * Precise timing for odd field flag.
+ * Remaining CRTC registers.
+ * Interlaced modes.
***************************************************************************/
#include "emu.h"
#include "nubus_48gc.h"
+
+#include "layout/generic.h"
#include "screen.h"
#include <algorithm>
+#define LOG_CRTC (1U << 1)
+#define LOG_RAMDAC (1U << 2)
+#define LOG_CLUT (1U << 3)
+#define LOG_CLKGEN (1U << 4)
+
+//#define VERBOSE (LOG_GENERAL | LOG_CRTC | LOG_RAMDAC | LOG_CLKGEN)
+//#define LOG_OUTPUT_FUNC osd_printf_error
+#include "logmacro.h"
+
+#define LOGCRTC(...) LOGMASKED(LOG_CRTC, __VA_ARGS__)
+#define LOGRAMDAC(...) LOGMASKED(LOG_RAMDAC, __VA_ARGS__)
+#define LOGCLUT(...) LOGMASKED(LOG_CLUT, __VA_ARGS__)
+#define LOGCLKGEN(...) LOGMASKED(LOG_CLKGEN, __VA_ARGS__)
-#define VRAM_SIZE (0x200000) // 2 megs, maxed out
-#define GC48_SCREEN_NAME "48gc_screen"
+#define GC48_SCREEN_NAME "screen"
#define GC48_ROM_REGION "48gc_rom"
+namespace {
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+// ======================> jmfb_device
+
+class jmfb_device :
+ public device_t,
+ public device_nubus_card_interface,
+ public device_video_interface,
+ public device_palette_interface
+{
+protected:
+ // construction/destruction
+ jmfb_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock);
+
+ // device-level overrides
+ virtual void device_start() override;
+ virtual void device_reset() override;
+
+ // optional information overrides
+ virtual void device_add_mconfig(machine_config &config) override;
+
+ // palette implementation
+ uint32_t palette_entries() const noexcept override;
+
+private:
+ static constexpr offs_t VRAM_MAX = 0x10'0000 / 4; // chip supports 2M but card can only use 1M
+
+ TIMER_CALLBACK_MEMBER(vbl_start);
+ void set_vbl_timer();
+
+ uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
+ template <uint8_t Mode>
+ void update_screen(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
+ template <uint8_t Mode, bool Convolution, bool Mono>
+ void update_screen(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
+ void update_crtc();
+
+ uint32_t jmfb_r(offs_t offset);
+ uint32_t crtc_r(offs_t offset);
+ uint32_t ramdac_r(offs_t offset);
+ void jmfb_w(offs_t offset, uint32_t data);
+ void crtc_w(offs_t offset, uint32_t data);
+ void ramdac_w(offs_t offset, uint32_t data);
+ void clkgen_w(offs_t offset, uint32_t data);
+
+ uint32_t rgb_unpack(offs_t offset, uint32_t mem_mask = ~0);
+ void rgb_pack(offs_t offset, uint32_t data, uint32_t mem_mask = ~0);
+
+ bool ctrl_sense2() const { return BIT(m_control, 11); }
+ bool ctrl_sense1() const { return BIT(m_control, 10); }
+ bool ctrl_sense0() const { return BIT(m_control, 9); }
+ bool ctrl_transfer() const { return BIT(m_control, 6); }
+ bool ctrl_convolution() const { return BIT(m_control, 5); }
+ bool ctrl_interlace() const { return BIT(m_control, 4); }
+ bool ctrl_rgb() const { return BIT(m_control, 2); }
+
+ required_ioport m_config;
+ memory_view m_vram_view;
+ emu_timer *m_timer;
+
+ bool m_configured;
+ bool m_clut_addr_read;
+ uint8_t m_monitor_type;
+
+ std::unique_ptr<uint32_t []> m_vram;
+ uint16_t m_control;
+ uint16_t m_preload;
+ uint32_t m_base, m_stride;
+
+ uint8_t m_colors[3], m_clutcnt, m_clutoffs;
+ uint8_t m_ramdac_mode, m_ramdac_conv;
+
+ uint16_t m_hhalf, m_hactive, m_hbporch, m_hsync, m_hfporch;
+ uint16_t m_vactive, m_vbporch, m_vsync, m_vfporch;
+ uint32_t m_vbl_disable;
+ uint16_t m_halfline_pixels;
+
+ uint16_t m_multiplier;
+ uint16_t m_modulus;
+ uint8_t m_pdiv;
+};
+
+class nubus_48gc_device : public jmfb_device
+{
+public:
+ nubus_48gc_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+
+protected:
+ // optional information overrides
+ virtual const tiny_rom_entry *device_rom_region() const override;
+ virtual ioport_constructor device_input_ports() const override;
+
+private:
+ void mac_48gc_w(offs_t offset, uint32_t data, uint32_t mem_mask = ~0);
+};
+
+class nubus_824gc_device : public jmfb_device
+{
+public:
+ nubus_824gc_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+
+protected:
+ // optional information overrides
+ virtual const tiny_rom_entry *device_rom_region() const override;
+ virtual ioport_constructor device_input_ports() const override;
+};
+
+
+INPUT_PORTS_START( 48gc )
+ PORT_START("CONFIG")
+ PORT_CONFNAME(0x0f, 0x06, u8"Attached monitor")
+ PORT_CONFSETTING( 0x00, u8"Macintosh Two-Page Monitor (1152\u00d7870)")
+ PORT_CONFSETTING( 0x01, u8"Macintosh Portrait Display (B&W 15\" 640\u00d7870)")
+ PORT_CONFSETTING( 0x02, u8"Macintosh RGB Display (12\" 512\u00d7384)")
+ PORT_CONFSETTING( 0x03, u8"Macintosh Two-Page Monitor (B&W 21\" 1152\u00d7870)")
+ PORT_CONFSETTING( 0x04, u8"NTSC Monitor (512\u00d7384, 640\u00d7480)") // requires interlace modes
+ PORT_CONFSETTING( 0x05, u8"Macintosh Portrait Display (640\u00d7870)")
+ PORT_CONFSETTING( 0x06, u8"Macintosh Hi-Res Display (12-14\" 640\u00d7480)")
+ PORT_CONFSETTING( 0x0b, u8"NTSC Encoder (512\u00d7384, 640\u00d7480)") // requires interlace modes
+ PORT_CONFNAME(0x10, 0x00, u8"VRAM size")
+ PORT_CONFSETTING( 0x00, u8"512 kB (4\u20228)")
+ PORT_CONFSETTING( 0x10, u8"1 MB (8\u202224)")
+ PORT_CONFNAME(0x20, 0x00, u8"CLUT address read")
+ PORT_CONFSETTING( 0x00, "Disable")
+ PORT_CONFSETTING( 0x20, "Enable")
+INPUT_PORTS_END
+
+
+INPUT_PORTS_START( 824gc )
+ PORT_START("CONFIG")
+ PORT_CONFNAME(0x0f, 0x06, u8"Attached monitor")
+ PORT_CONFSETTING( 0x00, u8"Mac 21\" Color Display (1152\u00d7870)")
+ PORT_CONFSETTING( 0x01, u8"Mac Portrait Display (B&W 15\" 640\u00d7870)")
+ PORT_CONFSETTING( 0x02, u8"Mac RGB Display (12\" 512\u00d7384)")
+ PORT_CONFSETTING( 0x03, u8"Mac Two-Page Display (B&W 21\" 1152\u00d7870)")
+ PORT_CONFSETTING( 0x04, u8"NTSC Monitor (512\u00d7384, 640\u00d7480)") // requires interlace modes
+ PORT_CONFSETTING( 0x06, u8"Mac Hi-Res Display (12-14\" 640\u00d7480)")
+ PORT_CONFSETTING( 0x0a, u8"PAL Encoder (640\u00d7480, 768\u00d7576)") // requires interlace modes
+ PORT_CONFSETTING( 0x0b, u8"NTSC Encoder (512\u00d7384, 640\u00d7480)") // requires interlace modes
+ PORT_CONFSETTING( 0x0d, u8"Mac 16\" Color Display (832\u00d7624)")
+ PORT_CONFSETTING( 0x1e, u8"PAL Monitor (640\u00d7480, 768\u00d7576)") // requires interlace modes
+ PORT_CONFNAME(0x10, 0x10, u8"VRAM size")
+ PORT_CONFSETTING( 0x00, u8"512 kB (4\u20228)")
+ PORT_CONFSETTING( 0x10, u8"1 MB (8\u202224)")
+ PORT_CONFNAME(0x20, 0x00, u8"CLUT address read")
+ PORT_CONFSETTING( 0x00, "Disable")
+ PORT_CONFSETTING( 0x20, "Enable")
+INPUT_PORTS_END
+
+
ROM_START( gc48 )
ROM_REGION(0x8000, GC48_ROM_REGION, 0)
ROM_LOAD( "3410801.bin", 0x0000, 0x8000, CRC(e283da91) SHA1(4ae21d6d7bbaa6fc7aa301bee2b791ed33b1dcf9) )
@@ -30,12 +237,29 @@ ROM_START( gc824 )
ROM_LOAD( "3410868.bin", 0x000000, 0x008000, CRC(57f925fa) SHA1(4d3c0632711b7b31c8e0c5cfdd7ec1904f178336) ) /* Label: "341-0868 // (C)APPLE COMPUTER // INC. 1986-1991 // ALL RIGHTS // RESERVED W5" */
ROM_END
-//**************************************************************************
-// GLOBAL VARIABLES
-//**************************************************************************
-DEFINE_DEVICE_TYPE(NUBUS_48GC, nubus_48gc_device, "nb_48gc", "Apple 4*8 video card")
-DEFINE_DEVICE_TYPE(NUBUS_824GC, nubus_824gc_device, "nb_824gc", "Apple 8*24 video card")
+// TODO: find a better place for this table to live
+struct mac_monitor_info { bool mono; unsigned sense[4]; };
+mac_monitor_info const f_monitors[] = {
+ // 512×385 640×480 640×870 832×624 1024×768 1152×870 640×480
+ // 60.15 Hz 66.67 Hz 75.08 Hz 74.55 Hz 74.93 Hz 75.08 Hz 59.94 Hz 55.98 Hz 59.94 Hz 50.00 Hz
+ // portrait interlaced interlaced
+ { false, { 0, 0, 0, 0 } }, // 0: RGB 21" yes
+ { true, { 1, 1, 1, 0 } }, // 1: Full-Page (B&W 15") yes
+ { false, { 2, 2, 0, 2 } }, // 2: RGB 12" yes
+ { true, { 3, 3, 1, 2 } }, // 3: Two-Page (B&W 21") yes
+ { false, { 4, 0, 4, 4 } }, // 4: NTSC Monitor yes
+ { false, { 5, 1, 5, 4 } }, // 5: RGB 15" yes
+ { false, { 6, 2, 4, 6 } }, // 6: Hi-Res (12-14") yes
+ { false, { 6, 0, 0, 6 } }, // 7: Multiple Scan 14" yes yes
+ { false, { 6, 0, 4, 6 } }, // 8: Multiple Scan 16" yes yes yes
+ { false, { 6, 2, 0, 6 } }, // 9: Multiple Scan 21" yes yes yes yes
+ { false, { 7, 0, 0, 0 } }, // 10: PAL Encoder yes
+ { false, { 7, 1, 1, 0 } }, // 11: NTSC Encoder yes
+ { false, { 7, 1, 1, 6 } }, // 12: VGA/Super VGA yes yes
+ { false, { 7, 2, 5, 2 } }, // 13: RGB 16" yes
+ { false, { 7, 3, 0, 0 } }, // 14: PAL Monitor yes
+ { false, { 7, 3, 4, 4 } } }; // 15: RGB 19" yes
//-------------------------------------------------
@@ -44,20 +268,20 @@ DEFINE_DEVICE_TYPE(NUBUS_824GC, nubus_824gc_device, "nb_824gc", "Apple 8*24 vide
void jmfb_device::device_add_mconfig(machine_config &config)
{
+ config.set_default_layout(layout_monitors);
+
screen_device &screen(SCREEN(config, GC48_SCREEN_NAME, SCREEN_TYPE_RASTER));
screen.set_screen_update(FUNC(jmfb_device::screen_update));
- screen.set_raw(25175000, 800, 0, 640, 525, 0, 480);
-// screen.set_size(1152, 870);
-// screen.set_visarea(0, 1152-1, 0, 870-1);
-// screen.set_refresh_hz(75);
-// screen.set_vblank_time(ATTOSECONDS_IN_USEC(1260));
+ screen.set_raw(20_MHz_XTAL / 21 * 127 / 4, 864, 0, 640, 525, 0, 480);
+ //screen.set_raw(20_MHz_XTAL / 19 * 190 / 2, 1'456, 0, 1'152, 915, 0, 870);
+ screen.set_video_attributes(VIDEO_UPDATE_SCANLINE);
}
//-------------------------------------------------
// rom_region - device-specific ROM region
//-------------------------------------------------
-const tiny_rom_entry *jmfb_device::device_rom_region() const
+const tiny_rom_entry *nubus_48gc_device::device_rom_region() const
{
return ROM_NAME( gc48 );
}
@@ -67,6 +291,30 @@ const tiny_rom_entry *nubus_824gc_device::device_rom_region() const
return ROM_NAME( gc824 );
}
+//-------------------------------------------------
+// input_ports - device-specific input ports
+//-------------------------------------------------
+
+ioport_constructor nubus_48gc_device::device_input_ports() const
+{
+ return INPUT_PORTS_NAME( 48gc );
+}
+
+ioport_constructor nubus_824gc_device::device_input_ports() const
+{
+ return INPUT_PORTS_NAME( 824gc );
+}
+
+//-------------------------------------------------
+// palette_entries - entries in color palette
+//-------------------------------------------------
+
+uint32_t jmfb_device::palette_entries() const noexcept
+{
+ return 256;
+}
+
+
//**************************************************************************
// LIVE DEVICE
//**************************************************************************
@@ -75,23 +323,25 @@ const tiny_rom_entry *nubus_824gc_device::device_rom_region() const
// jmfb_device - constructor
//-------------------------------------------------
-jmfb_device::jmfb_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, bool is824) :
+jmfb_device::jmfb_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) :
device_t(mconfig, type, tag, owner, clock),
- device_video_interface(mconfig, *this),
device_nubus_card_interface(mconfig, *this),
- m_screen(nullptr), m_timer(nullptr), m_mode(0), m_vbl_disable(0), m_toggle(0), m_stride(0), m_base(0), m_count(0), m_clutoffs(0), m_xres(0), m_yres(0),
- m_is824(is824)
+ device_video_interface(mconfig, *this),
+ device_palette_interface(mconfig, *this),
+ m_config(*this, "CONFIG"),
+ m_vram_view(*this, "vram"),
+ m_timer(nullptr)
{
set_screen(*this, GC48_SCREEN_NAME);
}
nubus_48gc_device::nubus_48gc_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
- jmfb_device(mconfig, NUBUS_48GC, tag, owner, clock, false)
+ jmfb_device(mconfig, NUBUS_MDC48, tag, owner, clock)
{
}
nubus_824gc_device::nubus_824gc_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
- jmfb_device(mconfig, NUBUS_824GC, tag, owner, clock, true)
+ jmfb_device(mconfig, NUBUS_MDC824, tag, owner, clock)
{
}
@@ -101,21 +351,61 @@ nubus_824gc_device::nubus_824gc_device(const machine_config &mconfig, const char
void jmfb_device::device_start()
{
- uint32_t slotspace;
+ install_declaration_rom(GC48_ROM_REGION);
+
+ uint32_t const slotspace = get_slotspace();
+
+ LOG("[JMFB %p] slotspace = %x\n", this, slotspace);
- install_declaration_rom(this, GC48_ROM_REGION);
+ m_vram = std::make_unique<uint32_t []>(VRAM_MAX);
+ install_view(slotspace, slotspace + 0x1f'ffff, m_vram_view);
- slotspace = get_slotspace();
+ nubus().install_device(
+ slotspace + 0x20'0000, slotspace + 0x20'000f,
+ read32sm_delegate(*this, FUNC(jmfb_device::jmfb_r)), write32sm_delegate(*this, FUNC(jmfb_device::jmfb_w)));
+ nubus().install_device(
+ slotspace + 0x20'0100, slotspace + 0x20'01ff,
+ read32sm_delegate(*this, FUNC(jmfb_device::crtc_r)), write32sm_delegate(*this, FUNC(jmfb_device::crtc_w)));
+ nubus().install_device(
+ slotspace + 0x200200, slotspace + 0x20020f,
+ read32sm_delegate(*this, FUNC(jmfb_device::ramdac_r)), write32sm_delegate(*this, FUNC(jmfb_device::ramdac_w)));
+ nubus().install_writeonly_device(
+ slotspace + 0x200300, slotspace + 0x20033f,
+ write32sm_delegate(*this, FUNC(jmfb_device::clkgen_w)));
-// printf("[JMFB %p] slotspace = %x\n", this, slotspace);
+ m_timer = timer_alloc(FUNC(jmfb_device::vbl_start), this);
- m_vram.resize(VRAM_SIZE);
- install_bank(slotspace, slotspace+VRAM_SIZE-1, "bank_48gc", &m_vram[0]);
+ m_configured = false;
+ m_clut_addr_read = false;
+ m_monitor_type = 0;
- nubus().install_device(slotspace+0x200000, slotspace+0x2003ff, read32s_delegate(*this, FUNC(jmfb_device::mac_48gc_r)), write32s_delegate(*this, FUNC(jmfb_device::mac_48gc_w)));
+ m_ramdac_mode = 0;
+ m_ramdac_conv = 0;
- m_timer = timer_alloc(0, nullptr);
- m_screen = nullptr; // can we look this up now?
+ save_item(NAME(m_monitor_type));
+ save_pointer(NAME(m_vram), VRAM_MAX);
+ save_item(NAME(m_control));
+ save_item(NAME(m_preload));
+ save_item(NAME(m_base));
+ save_item(NAME(m_stride));
+ save_item(NAME(m_colors));
+ save_item(NAME(m_clutcnt));
+ save_item(NAME(m_clutoffs));
+ save_item(NAME(m_ramdac_mode));
+ save_item(NAME(m_ramdac_conv));
+ save_item(NAME(m_hactive));
+ save_item(NAME(m_hbporch));
+ save_item(NAME(m_hsync));
+ save_item(NAME(m_hfporch));
+ save_item(NAME(m_vactive));
+ save_item(NAME(m_vbporch));
+ save_item(NAME(m_vsync));
+ save_item(NAME(m_vfporch));
+ save_item(NAME(m_vbl_disable));
+ save_item(NAME(m_halfline_pixels));
+ save_item(NAME(m_multiplier));
+ save_item(NAME(m_modulus));
+ save_item(NAME(m_pdiv));
}
//-------------------------------------------------
@@ -124,17 +414,99 @@ void jmfb_device::device_start()
void jmfb_device::device_reset()
{
- m_toggle = 0;
- m_clutoffs = 0;
- m_count = 0;
+ if (!m_configured)
+ {
+ m_configured = true;
+ ioport_value const config = m_config->read();
+
+ m_monitor_type = config & 0x0f;
+ if (m_monitor_type > std::size(f_monitors))
+ {
+ throw emu_fatalerror("%s: Invalid monitor selection %d\n", tag(), m_monitor_type);
+ }
+
+ uint32_t const slotspace = get_slotspace();
+ uint32_t const vramsize = VRAM_MAX * 4 / (BIT(config, 4) ? 1 : 2);
+ m_vram_view[0].install_ram(slotspace, slotspace + vramsize - 1, &m_vram[0]);
+ m_vram_view[1].install_readwrite_handler(
+ slotspace, slotspace + (vramsize / 3 * 4) - 1,
+ read32s_delegate(*this, FUNC(jmfb_device::rgb_unpack)), write32s_delegate(*this, FUNC(jmfb_device::rgb_pack)));
+ switch (vramsize % 3)
+ {
+ case 0:
+ break;
+ case 1:
+ m_vram_view[1].install_readwrite_handler(
+ slotspace + (vramsize / 3 * 4), slotspace + (vramsize / 3 * 4) + 3,
+ read32s_delegate(
+ *this,
+ NAME(([this, vramsize] (offs_t offset, uint32_t mem_mask) -> uint32_t
+ {
+ auto const color = util::big_endian_cast<uint8_t const>(&m_vram[0]) + (vramsize - 1);
+ return uint32_t(color[0]) << 16;
+ }))),
+ write32s_delegate(
+ *this,
+ NAME(([this, vramsize] (offs_t offset, uint32_t data, uint32_t mem_mask)
+ {
+ auto const color = util::big_endian_cast<uint8_t>(&m_vram[0]) + (vramsize - 1);
+ if (ACCESSING_BITS_16_23)
+ color[0] = uint8_t(data >> 16);
+ }))));
+ break;
+ case 2:
+ m_vram_view[1].install_readwrite_handler(
+ slotspace + (vramsize / 3 * 4), slotspace + (vramsize / 3 * 4) + 3,
+ read32s_delegate(
+ *this,
+ NAME(([this, vramsize] (offs_t offset, uint32_t mem_mask) -> uint32_t
+ {
+ auto const color = util::big_endian_cast<uint8_t const>(&m_vram[0]) + (vramsize - 2);
+ return (uint32_t(color[0]) << 16) | (uint32_t(color[1]) << 8);
+ }))),
+ write32s_delegate(
+ *this,
+ NAME(([this, vramsize] (offs_t offset, uint32_t data, uint32_t mem_mask)
+ {
+ auto const color = util::big_endian_cast<uint8_t>(&m_vram[0]) + (vramsize - 2);
+ if (ACCESSING_BITS_16_23)
+ color[0] = uint8_t(data >> 16);
+ if (ACCESSING_BITS_8_15)
+ color[1] = uint8_t(data >> 8);
+ }))));
+ break;
+ }
+
+ m_clut_addr_read = BIT(config, 5);
+ }
+
+ m_vram_view.select(0);
+
+ std::fill_n(&m_vram[0], VRAM_MAX, 0);
m_vbl_disable = 1;
- m_stride = 80;
+ m_control = 0x0002;
+ m_preload = 256 - 8;
m_base = 0;
- m_xres = 640;
- m_yres = 480;
- m_mode = 0;
- memset(&m_vram[0], 0, VRAM_SIZE);
- memset(m_palette, 0, sizeof(m_palette));
+ m_stride = 80 / 4;
+
+ m_clutoffs = 0;
+ m_clutcnt = 0;
+ m_ramdac_mode = 0;
+ m_ramdac_conv = 0;
+
+ m_hactive = 286;
+ m_hbporch = 22;
+ m_hsync = 30;
+ m_hfporch = 18;
+ m_vactive = 1740;
+ m_vbporch = 78;
+ m_vsync = 6;
+ m_vfporch = 6;
+ m_halfline_pixels = 576;
+
+ m_multiplier = 190;
+ m_modulus = 19;
+ m_pdiv = 1;
}
/***************************************************************************
@@ -143,212 +515,656 @@ void jmfb_device::device_reset()
***************************************************************************/
-void jmfb_device::device_timer(emu_timer &timer, device_timer_id tid, int param, void *ptr)
+TIMER_CALLBACK_MEMBER(jmfb_device::vbl_start)
{
if (!m_vbl_disable)
{
raise_slot_irq();
}
- m_timer->adjust(m_screen->time_until_pos(479, 0), 0);
+ set_vbl_timer();
+}
+
+void jmfb_device::set_vbl_timer()
+{
+ // TODO: precise VBL timing in interlaced modes if half-line split is offset (likely doesn't matter in practice)
+ rectangle const &visarea = screen().visible_area();
+ int const height = screen().height();
+ if ((visarea.bottom() + 1) < height)
+ {
+ m_timer->adjust(screen().time_until_pos(visarea.bottom() + 1));
+ }
+ else
+ {
+ m_timer->adjust(screen().time_until_pos(visarea.bottom() + 1 - height));
+ }
}
uint32_t jmfb_device::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
- uint8_t const *const vram8 = &m_vram[0xa00];
+ if (!ctrl_transfer())
+ {
+ bitmap.fill(0, cliprect);
+ return 0;
+ }
- // first time? kick off the VBL timer
- if (!m_screen)
+ switch (m_ramdac_mode)
{
- m_screen = &screen;
- m_timer->adjust(m_screen->time_until_pos(479, 0), 0);
+ case 0x0: // 1bpp
+ update_screen<0x0>(screen, bitmap, cliprect);
+ break;
+
+ case 0x4: // 2bpp
+ update_screen<0x4>(screen, bitmap, cliprect);
+ break;
+
+ case 0x8: // 4 bpp
+ update_screen<0x8>(screen, bitmap, cliprect);
+ break;
+
+ case 0xc: // 8 bpp
+ update_screen<0xc>(screen, bitmap, cliprect);
+ break;
+
+ case 0xd: // 24 bpp
+ update_screen<0xd>(screen, bitmap, cliprect);
+ break;
+
+ default:
+ throw emu_fatalerror("%s: Unsupported RAMDAC mode %d\n", tag(), m_ramdac_mode);
}
- switch (m_mode)
+ return 0;
+}
+
+template <uint8_t Mode>
+void jmfb_device::update_screen(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
+{
+ if (!ctrl_convolution())
+ {
+ if (!f_monitors[m_monitor_type].mono)
+ update_screen<Mode, false, false>(screen, bitmap, cliprect);
+ else
+ update_screen<Mode, false, true>(screen, bitmap, cliprect);
+ }
+ else
{
- case 0: // 1bpp
- for (int y = 0; y < m_yres; y++)
+ if (!f_monitors[m_monitor_type].mono)
+ update_screen<Mode, true, false>(screen, bitmap, cliprect);
+ else
+ update_screen<Mode, true, true>(screen, bitmap, cliprect);
+ }
+}
+
+template <uint8_t Mode, bool Convolution, bool Mono>
+void jmfb_device::update_screen(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
+{
+ // TODO: interlaced mode
+ auto const baseoffset = m_base << (((0xd == Mode) ? 6 : 5) + (Convolution ? 1 : 0));
+ auto const screenbase = util::big_endian_cast<uint8_t const>(&m_vram[0]) + baseoffset;
+ auto const stride = m_stride << ((0xd == Mode) ? 3 : 2);
+
+ auto const trans =
+ [] (rgb_t color)
+ {
+ return !Mono ? color : rgb_t(color.b(), color.b(), color.b());
+ };
+
+ rectangle const &visarea = screen.visible_area();
+ int const xstart = visarea.left();
+ int xres = visarea.width();
+ if (0x0 == Mode)
+ {
+ xres /= 8;
+ }
+ else if (0x4 == Mode)
+ {
+ xres /= 4;
+ }
+ else if (0x8 == Mode)
+ {
+ xres /= 2;
+ }
+
+ int y = cliprect.top();
+ while (y < visarea.top())
+ {
+ std::fill_n(&bitmap.pix(y++, xstart), visarea.width(), 0);
+ }
+ while ((y <= cliprect.bottom()) && (y <= visarea.bottom()))
+ {
+ auto source = screenbase + ((y - visarea.top()) * stride);
+ uint32_t *scanline = &bitmap.pix(y, xstart);
+ y++;
+ for (int x = 0; x <= xres; x++)
+ {
+ if (0xd == Mode) // 24bpp
{
- uint32_t *scanline = &bitmap.pix(y);
- for (int x = 0; x < m_xres/8; x++)
- {
- uint8_t const pixels = vram8[(y * m_stride) + (BYTE4_XOR_BE(x))];
-
- *scanline++ = m_palette[BIT(pixels, 7)];
- *scanline++ = m_palette[BIT(pixels, 6)];
- *scanline++ = m_palette[BIT(pixels, 5)];
- *scanline++ = m_palette[BIT(pixels, 4)];
- *scanline++ = m_palette[BIT(pixels, 3)];
- *scanline++ = m_palette[BIT(pixels, 2)];
- *scanline++ = m_palette[BIT(pixels, 1)];
- *scanline++ = m_palette[BIT(pixels, 0)];
- }
+ if (!f_monitors[m_monitor_type].mono)
+ *scanline++ = rgb_t(source[0], source[1], source[2]);
+ else
+ *scanline++ = rgb_t(source[2], source[2], source[2]);
+ source += 3;
}
- break;
-
- case 1: // 2bpp
- for (int y = 0; y < m_yres; y++)
+ else if (!Convolution)
{
- uint32_t *scanline = &bitmap.pix(y);
- for (int x = 0; x < m_xres/4; x++)
- {
- uint8_t const pixels = vram8[(y * m_stride) + (BYTE4_XOR_BE(x))];
+ uint8_t const pixels = *source++;
- *scanline++ = m_palette[(pixels>>6)&0x3];
- *scanline++ = m_palette[(pixels>>4)&0x3];
- *scanline++ = m_palette[(pixels>>2)&0x3];
- *scanline++ = m_palette[pixels&3];
+ if (0x0 == Mode) // 1bpp
+ {
+ *scanline++ = trans(pen_color(BIT(pixels, 7)));
+ *scanline++ = trans(pen_color(BIT(pixels, 6)));
+ *scanline++ = trans(pen_color(BIT(pixels, 5)));
+ *scanline++ = trans(pen_color(BIT(pixels, 4)));
+ *scanline++ = trans(pen_color(BIT(pixels, 3)));
+ *scanline++ = trans(pen_color(BIT(pixels, 2)));
+ *scanline++ = trans(pen_color(BIT(pixels, 1)));
+ *scanline++ = trans(pen_color(BIT(pixels, 0)));
}
- }
- break;
-
- case 2: // 4 bpp
- for (int y = 0; y < m_yres; y++)
- {
- uint32_t *scanline = &bitmap.pix(y);
- for (int x = 0; x < m_xres/2; x++)
+ else if (0x4 == Mode) // 2bpp
{
- uint8_t const pixels = vram8[(y * m_stride) + (BYTE4_XOR_BE(x))];
-
- *scanline++ = m_palette[(pixels>>4)&0xf];
- *scanline++ = m_palette[pixels&0xf];
+ *scanline++ = trans(pen_color(BIT(pixels, 6, 2)));
+ *scanline++ = trans(pen_color(BIT(pixels, 4, 2)));
+ *scanline++ = trans(pen_color(BIT(pixels, 2, 2)));
+ *scanline++ = trans(pen_color(BIT(pixels, 0, 2)));
+ }
+ else if (0x8 == Mode) // 4bpp
+ {
+ *scanline++ = trans(pen_color(BIT(pixels, 4, 4)));
+ *scanline++ = trans(pen_color(BIT(pixels, 0, 4)));
+ }
+ else if (0xc == Mode) // 8bpp
+ {
+ *scanline++ = trans(pen_color(pixels));
}
}
- break;
-
- case 3: // 8 bpp
- for (int y = 0; y < m_yres; y++)
+ else
{
- uint32_t *scanline = &bitmap.pix(y);
- for (int x = 0; x < m_xres; x++)
+ uint8_t const pixabove = source[0 * stride];
+ uint8_t const pixels = source[1 * stride];
+ uint8_t const pixbelow = source[2 * stride];
+ source++;
+
+ if (0x0 == Mode) // 1bpp
{
- uint8_t const pixels = vram8[(y * m_stride) + (BYTE4_XOR_BE(x))];
- *scanline++ = m_palette[pixels];
+ for (int p = 7; p >= 0; p--)
+ {
+ rgb_t const a = pen_color(BIT(pixabove, p));
+ rgb_t const b = pen_color(BIT(pixels, p));
+ rgb_t const c = pen_color(BIT(pixbelow, p));
+ *scanline++ = trans(
+ rgb_t(
+ (a.r() + (uint16_t(b.r()) << 1) + c.r() + 2) >> 2,
+ (a.g() + (uint16_t(b.g()) << 1) + c.g() + 2) >> 2,
+ (a.b() + (uint16_t(b.b()) << 1) + c.b() + 2) >> 2));
+ }
+ }
+ else if (0x4 == Mode) // 2bpp
+ {
+ for (int p = 6; p >= 0; p -= 2)
+ {
+ rgb_t const a = pen_color(BIT(pixabove, p, 2));
+ rgb_t const b = pen_color(BIT(pixels, p, 2));
+ rgb_t const c = pen_color(BIT(pixbelow, p, 2));
+ *scanline++ = trans(
+ rgb_t(
+ (a.r() + (uint16_t(b.r()) << 1) + c.r() + 2) >> 2,
+ (a.g() + (uint16_t(b.g()) << 1) + c.g() + 2) >> 2,
+ (a.b() + (uint16_t(b.b()) << 1) + c.b() + 2) >> 2));
+ }
+ }
+ else if (0x8 == Mode) // 4bpp
+ {
+ for (int p = 4; p >= 0; p -= 4)
+ {
+ rgb_t const a = pen_color(BIT(pixabove, p, 4));
+ rgb_t const b = pen_color(BIT(pixels, p, 4));
+ rgb_t const c = pen_color(BIT(pixbelow, p, 4));
+ *scanline++ = trans(
+ rgb_t(
+ (a.r() + (uint16_t(b.r()) << 1) + c.r() + 2) >> 2,
+ (a.g() + (uint16_t(b.g()) << 1) + c.g() + 2) >> 2,
+ (a.b() + (uint16_t(b.b()) << 1) + c.b() + 2) >> 2));
+ }
+ }
+ else if (0xc == Mode) // 8bpp
+ {
+ rgb_t const a = pen_color(pixabove);
+ rgb_t const b = pen_color(pixels);
+ rgb_t const c = pen_color(pixbelow);
+ *scanline++ = trans(
+ rgb_t(
+ (a.r() + (uint16_t(b.r()) << 1) + c.r() + 2) >> 2,
+ (a.g() + (uint16_t(b.g()) << 1) + c.g() + 2) >> 2,
+ (a.b() + (uint16_t(b.b()) << 1) + c.b() + 2) >> 2));
}
}
- break;
+ }
+ }
+ while (y <= cliprect.bottom())
+ {
+ std::fill_n(&bitmap.pix(y++, xstart), visarea.width(), 0);
+ }
+}
- case 4: // 24 bpp
- for (int y = 0; y < m_yres; y++)
- {
- uint32_t const *base = (uint32_t *)&m_vram[y * m_stride];
- std::copy_n(base, m_xres, &bitmap.pix(y));
- }
+void jmfb_device::update_crtc()
+{
+ // Vertical values are always in half-lines.
+ // In progressive modes, we give the screen device numbers of full lines.
+ // In interlaced modes we let the screen device base horizontal timing on half-lines.
+ int const vtotal = m_vactive + m_vbporch + m_vsync + m_vfporch;
+ if (vtotal && m_vactive && m_multiplier && m_modulus)
+ {
+ bool const interlace = vtotal % 2;
+ bool const convolution = ctrl_convolution();
+
+ int const vstart = m_vsync + m_vbporch;
+ int const vlines = vtotal >> (interlace ? 0 : 1);
+ int const top = vstart >> (interlace ? 0 : 1);
+ int const height = m_vactive >> (interlace ? 0 : 1);
+
+ int const divider = 256 - m_preload;
+ XTAL const refclk = 20_MHz_XTAL / m_modulus;
+ XTAL const vcoout = refclk * m_multiplier;
+ XTAL const pixclk = vcoout / (1 << m_pdiv);
+ XTAL const dacclk = pixclk / divider;
+ LOGCLKGEN("reference clock %d VCO output %d pixel clock %d RAMDAC longword load clock %d\n",
+ refclk.value(), vcoout.value(), pixclk.value(), dacclk.value());
+
+ int const htotal = m_hactive + m_hbporch + m_hsync + m_hfporch + 8;
+ int const hstart = m_hbporch + m_hsync + 4;
+ int const hactive = m_hactive + 2;
+
+ int scale = 0;
+ switch (m_ramdac_mode)
+ {
+ case 0x0: // 1bpp - 32 pixels/longword
+ scale = 5;
+ break;
+ case 0x4: // 2bpp - 16 pixels/longword
+ scale = 4;
+ break;
+ case 0x8: // 4bpp - 8 pixels/longword
+ scale = 3;
+ break;
+ case 0xc: // 8bpp - 4 pixels/longword
+ scale = 2;
break;
+ case 0xd: // 24bpp - 1 pixel/longword
+ scale = 0;
+ break;
+ }
+ int const hpixels = (htotal << scale >> (convolution ? 2 : 0)) / divider;
+ int const left = (hstart << scale >> (convolution ? 2 : 0)) / divider;
+ int const width = (hactive << scale >> (convolution ? 2 : 0)) / divider;
+ m_halfline_pixels = (m_hhalf << scale >> (convolution ? 2 : 0)) / divider;
+ LOGCRTC("vertical total %d start %d active %d horizontal total %d start %d active %d (mode %x %d/%d)\n",
+ vtotal, vstart, m_vactive, htotal, hstart, hactive, m_ramdac_mode, width, hpixels);
+
+ int const frametotal = hpixels * vlines;
+
+ screen().configure(
+ hpixels, vlines,
+ rectangle(left, left + width - 1, top, top + height - 1),
+ attotime::from_ticks(frametotal << (convolution ? 2 : 0) >> (interlace ? 1 : 0), pixclk).attoseconds());
+
+ set_vbl_timer();
}
+}
- return 0;
+uint32_t jmfb_device::jmfb_r(offs_t offset)
+{
+ switch (offset)
+ {
+ case 0x00/4:
+ {
+ uint16_t sense = f_monitors[m_monitor_type].sense[0];
+ if (ctrl_sense2())
+ sense &= f_monitors[m_monitor_type].sense[1];
+ if (ctrl_sense1())
+ sense &= f_monitors[m_monitor_type].sense[2];
+ if (ctrl_sense0())
+ sense &= f_monitors[m_monitor_type].sense[3];
+ return (m_control & 0xf1ff) | (sense << 9);
+ }
+
+ case 0x04/4: return m_preload; // RAMDAC longword load clock divider preload
+ case 0x08/4: return m_base; // base - 32-byte increments for indexed, 64-byte increments for direct
+ case 0x0c/4: return m_stride; // stride - 4-byte increments for indexed, 8-byte increments for direct
+
+ default:
+ LOG("%s: read unimplemented JMFB register %x/4\n", machine().describe_context(), offset * 4);
+ return 0;
+ }
}
-void jmfb_device::mac_48gc_w(offs_t offset, uint32_t data, uint32_t mem_mask)
+uint32_t jmfb_device::crtc_r(offs_t offset)
{
- COMBINE_DATA(&m_registers[offset&0xff]);
+// printf("%s crtc_r: @ %x, mask %08x\n", machine().describe_context().c_str(), offset, mem_mask);
switch (offset)
{
- case 0x8/4: // base
-// printf("%x to base\n", data);
- m_base = (data*2)<<4;
- break;
+ case 0x08/4: return m_hhalf; // half line length from start of active area
+ case 0x0c/4: return m_hactive; // active pixel cells - 2
+ case 0x10/4: return m_hbporch; // horizontal back porch - 2
+ case 0x14/4: return m_hsync; // horizontal sync pulse width - 2
+ case 0x18/4: return m_hfporch; // horizontal front porch - 2
- case 0xc/4: // stride
-// printf("%x to stride\n", data);
- // this value is in DWORDs for 1-8 bpp and, uhh, strange for 24bpp
- if (m_mode < 4)
+ case 0x24/4: return m_vactive; // active lines * 2
+ case 0x28/4: return m_vbporch; // vertical back porch * 2
+ case 0x2c/4: return m_vsync; // vertical sync pulse width * 2
+ case 0x30/4: return m_vfporch; // vertical front porch * 2
+
+ case 0xc0/4: // beam position/status
+ {
+ // TODO: remaining two flags - interrupt status?
+ rectangle const &visarea = screen().visible_area();
+ int const vpos = screen().vpos();
+ int const hpos = screen().hpos();
+ int const hsplit = visarea.left() + m_halfline_pixels;
+ int const vtotal = m_vactive + m_vbporch + m_vsync + m_vfporch;
+ uint8_t result = 0x0f;
+
+ int halfline;
+ int truehpos;
+ if (vtotal % 2)
{
- m_stride = data*4;
+ // screen device configured to count half-lines vertically
+ int const oddfield = screen().frame_number() % 2;
+ int const oddhalfline = vpos % 2;
+ truehpos = (hpos + ((oddfield != oddhalfline) ? screen().width() : 0)) >> 1;
+
+ // adjust half line count in case half line position is offset
+ if ((oddfield == oddhalfline) && (truehpos >= hsplit))
+ {
+ halfline = vpos + 1;
+ if (halfline >= vtotal)
+ {
+ halfline -= vtotal;
+ }
+ }
+ else if ((oddfield != oddhalfline) && (truehpos < hsplit))
+ {
+ halfline = vpos - 1;
+ if (halfline < 0)
+ {
+ halfline += vtotal;
+ }
+ }
+ else
+ {
+ halfline = vpos;
+ }
+
+ // set odd field flag
+ if (oddfield)
+ {
+ // TODO: confirm where this flips - on sync or on front porch?
+ result |= 0x10; // odd field
+ }
}
else
{
- m_stride = (data*32)/3;
+ // vertical counts are in half-lines but screen device uses full lines
+ halfline = (vpos << 1) | ((hpos >= hsplit) ? 1 : 0);
+ truehpos = hpos;
}
- break;
- case 0x200/4: // DAC control
-// printf("%08x to DAC control\n", data);
- if (m_is824)
- {
- m_clutoffs = data&0xff;
- }
- else
+ // set horizontal beam position flag
+ if ((truehpos < visarea.left()) || (truehpos > visarea.right()))
{
- m_clutoffs = data>>24;
+ result |= 0x20; // horizontal blanking
}
- m_count = 0;
- break;
- case 0x204/4: // DAC data
- if (m_is824)
+ // set appropriate vertical beam position flag (active low)
+ if (halfline < m_vsync)
{
- m_colors[m_count++] = data&0xff;
+ result &= ~0x04; // sync pulse
}
- else
+ else if (halfline < (m_vsync + m_vbporch))
{
- m_colors[m_count++] = data>>24;
+ result &= ~0x02; // back porch
}
-
- if (m_count == 3)
+ else if (halfline < (m_vsync + m_vbporch + m_vactive))
{
-// printf("RAMDAC: color %d = %02x %02x %02x\n", m_clutoffs, m_colors[0], m_colors[1], m_colors[2]);
- m_palette[m_clutoffs] = rgb_t(m_colors[0], m_colors[1], m_colors[2]);
- m_clutoffs++;
- m_count = 0;
+ result &= ~0x01; // active
}
- break;
-
- case 0x208/4: // mode control
- m_mode = (data>>3)&3;
- if (m_mode == 3) // this can be 8 or 24 bpp
+ else
{
- // check pixel format for 24bpp
- if (m_is824)
- {
- if (data & 2)
- {
- m_mode = 4; // 24bpp
- }
- }
- else
- {
- if (((data>>5)&3) == 0)
- {
- m_mode = 4; // 24bpp
- }
- }
+ result &= ~0x08; // front porch
}
-// printf("%02x to mode (m_mode = %d)\n", data, m_mode);
- break;
- case 0x13c/4: // bit 1 = VBL disable (1=no interrupts)
- m_vbl_disable = (data & 2) ? 1 : 0;
- break;
+ return result;
+ }
- case 0x148/4: // write 1 here to clear interrupt
- if (data == 1)
- {
- lower_slot_irq();
- }
- break;
+ case 0xcc/4: // TODO: What is this? Waits for bit 3 to clear before clearing interrupts.
+ return 0;
- default:
- break;
+ default:
+ LOGCRTC("%s: read unimplemented CRTC register %02x/4\n", machine().describe_context(), offset * 4);
+ return 0;
}
}
-uint32_t jmfb_device::mac_48gc_r(offs_t offset, uint32_t mem_mask)
+uint32_t jmfb_device::ramdac_r(offs_t offset)
{
-// printf("%s 48gc_r: @ %x, mask %08x\n", machine().describe_context().c_str(), offset, mem_mask);
+ switch (offset)
+ {
+ case 0x00/4: // CLUT address
+ // The firmware explicitly checks whether it can read the value written to the CLUT address
+ // What difference this makes is not known
+ return m_clut_addr_read ? m_clutoffs : 0;
+
+ default:
+ LOGRAMDAC("%s: read unimplemented RAMDAC register %x/4\n", machine().describe_context(), offset * 4);
+ return 0;
+ }
+}
+void jmfb_device::jmfb_w(offs_t offset, uint32_t data)
+{
+ data &= 0xffff; // 16 bits wide, but lane select is ignored and firmware relies on smearing
switch (offset)
{
- case 0:
- return 0x0c00; // sense 13" RGB for now
-// return 0x0000; // sense "RGB Kong" monitor
+ case 0x00/4: // control
+ LOG("%s: %04x to control (%spixel clock %x sense %x transfer %x convolution %x interlace %x refresh %x RGB %x RAM %dk)\n",
+ machine().describe_context(), data,
+ BIT(data, 15) ? "reset " : "",
+ BIT(data, 12, 3),
+ BIT(data, 9, 3),
+ BIT(data, 6),
+ BIT(data, 5),
+ BIT(data, 4),
+ BIT(data, 3),
+ BIT(data, 2),
+ BIT(data, 0) ? 256 : 128);
+ m_control = data & 0x7fff; // video reset bit needs to read as zero
+ m_vram_view.select(ctrl_rgb() ? 1 : 0); // packed RGB mode
+ break;
+
+ case 0x04/4: // RAMDAC longword load clock divider preload
+ LOG("%s: 256-%d to preload\n", machine().describe_context(), data & 0xff);
+ m_preload = data & 0xff;
+ update_crtc();
+ break;
- case 0x1c0/4:
- m_toggle ^= 0xffffffff;
- return m_toggle;
+ case 0x08/4: // base - 32-byte increments for indexed, 64-byte increments for direct
+ LOG("%s: %x to base\n", machine().describe_context(), data);
+ m_base = data;
+ break;
+
+ case 0x0c/4: // stride - 4-byte increments for indexed, 8-byte increments for direct
+ LOG("%s: %x to stride\n", machine().describe_context(), data);
+ m_stride = data;
+ break;
}
+}
- return 0;
+void jmfb_device::crtc_w(offs_t offset, uint32_t data)
+{
+ data &= 0xffff; // 16 bits wide, but lane select is ignored and firmware relies on smearing
+ switch (offset)
+ {
+ case 0x08/4: // half line length from start of active area
+ LOGCRTC("%s: %d to half line cells\n", machine().describe_context(), data);
+ m_hhalf = data & 0x0fff;
+ update_crtc();
+ break;
+
+ case 0x0c/4: // active pixel cells - 2
+ LOGCRTC("%s: %d+2 to active cells\n", machine().describe_context(), data);
+ m_hactive = data & 0x0fff;
+ update_crtc();
+ break;
+
+ case 0x10/4: // horizontal back porch - 2
+ LOGCRTC("%s: %d+2 to horizontal back porch\n", machine().describe_context(), data);
+ m_hbporch = data & 0x0fff;
+ update_crtc();
+ break;
+
+ case 0x14/4: // horizontal sync pulse width - 2
+ LOGCRTC("%s: %d+2 to horizontal sync pulse width\n", machine().describe_context(), data);
+ m_hsync = data & 0x0fff;
+ update_crtc();
+ break;
+
+ case 0x18/4: // horizontal front porch - 2
+ LOGCRTC("%s: %d+2 to horizontal front porch\n", machine().describe_context(), data);
+ m_hfporch = data & 0x0fff;
+ update_crtc();
+ break;
+
+ case 0x24/4: // active lines * 2
+ LOGCRTC("%s: %d/2 to active lines\n", machine().describe_context(), data);
+ m_vactive = data & 0x0fff;
+ update_crtc();
+ break;
+
+ case 0x28/4: // vertical back porch * 2
+ LOGCRTC("%s: %d/2 to vertical back porch\n", machine().describe_context(), data);
+ m_vbporch = data & 0x0fff;
+ update_crtc();
+ break;
+
+ case 0x2c/4: // vertical sync pulse width * 2
+ LOGCRTC("%s: %d/2 to vertical sync pulse width\n", machine().describe_context(), data);
+ m_vsync = data & 0x0fff;
+ update_crtc();
+ break;
+
+ case 0x30/4: // vertical front porch * 2
+ LOGCRTC("%s: %d/2 to vertical front porch\n", machine().describe_context(), data);
+ m_vfporch = data & 0x0fff;
+ update_crtc();
+ break;
+
+ case 0x3c/4: // bit 1 = VBL disable (1=no interrupts)
+ m_vbl_disable = (data & 2) ? 1 : 0;
+ break;
+
+ case 0x48/4: // write here to clear interrupt
+ lower_slot_irq();
+ break;
+
+ default:
+ LOGCRTC("%s: %03x to unimplemented CRTC register %x/4\n", machine().describe_context(), data, offset * 4);
+ }
+}
+
+void jmfb_device::ramdac_w(offs_t offset, uint32_t data)
+{
+ data &= 0xff; // 8 bits wide, but lane select is ignored and firmware relies on smearing
+ switch (offset)
+ {
+ case 0x00/4: // CLUT address
+ LOGCLUT("%s: %u to RAMDAC color address\n", machine().describe_context(), data);
+ m_clutoffs = data;
+ m_clutcnt = 0;
+ break;
+
+ case 0x04/4: // CLUT data
+ m_colors[m_clutcnt++] = data & 0xff;
+ if (m_clutcnt == 3)
+ {
+ LOGCLUT("%s: RAMDAC color %u = %02x %02x %02x\n", machine().describe_context(), m_clutoffs, m_colors[0], m_colors[1], m_colors[2]);
+ set_pen_color(m_clutoffs, rgb_t(m_colors[0], m_colors[1], m_colors[2]));
+ m_clutoffs++;
+ m_clutcnt = 0;
+ }
+ break;
+
+ case 0x08/4: // RAMDAC mode control
+ m_ramdac_mode = (data >> 1) & 0xf;
+ m_ramdac_conv = BIT(data, 0);
+ LOGRAMDAC("%s: %02x to RAMDAC control (mode %x convolution %x)\n",
+ machine().describe_context(), data, m_ramdac_mode, m_ramdac_conv);
+ update_crtc();
+ break;
+
+ default:
+ LOGRAMDAC("%s: %02x to unimplemented RAMDAC register %x/4\n", machine().describe_context(), data, offset * 4);
+ }
+}
+
+void jmfb_device::clkgen_w(offs_t offset, uint32_t data)
+{
+ data &= 0x0f; // four bits wide, but lane select is ignored and firmware relies on smearing
+ switch (offset)
+ {
+ case 0x00/4:
+ case 0x04/4:
+ case 0x08/4:
+ case 0x0c/4:
+ m_multiplier &= ~(0x0f << ((offset & 3) * 4));
+ m_multiplier |= data << ((offset & 3) * 4);
+ LOGCLKGEN("%s: %d to multiplier\n", machine().describe_context(), m_multiplier);
+ update_crtc();
+ break;
+
+ case 0x10/4:
+ case 0x14/4:
+ case 0x18/4:
+ m_modulus &= ~(0x0f << ((offset & 3) * 4));
+ m_modulus |= data << ((offset & 3) * 4);
+ LOGCLKGEN("%s: %d to reference clock divider\n", machine().describe_context(), m_modulus);
+ update_crtc();
+ break;
+
+ case 0x24/4:
+ m_pdiv = data;
+ LOGCLKGEN("%s: 1<<%d to pixel clock divider\n", machine().describe_context(), m_pdiv);
+ update_crtc();
+ break;
+
+ default:
+ LOGCRTC("%s: %x to unimplemented clock synthesiser register %x/4\n", machine().describe_context(), data, offset * 4);
+ }
}
+
+uint32_t jmfb_device::rgb_unpack(offs_t offset, uint32_t mem_mask)
+{
+ auto const color = util::big_endian_cast<uint8_t const>(&m_vram[0]) + (offset * 3);
+ return (uint32_t(color[0]) << 16) | (uint32_t(color[1]) << 8) | uint32_t(color[2]);
+}
+
+void jmfb_device::rgb_pack(offs_t offset, uint32_t data, uint32_t mem_mask)
+{
+ auto const color = util::big_endian_cast<uint8_t>(&m_vram[0]) + (offset * 3);
+ if (ACCESSING_BITS_16_23)
+ color[0] = uint8_t(data >> 16);
+ if (ACCESSING_BITS_8_15)
+ color[1] = uint8_t(data >> 8);
+ if (ACCESSING_BITS_0_7)
+ color[2] = uint8_t(data);
+}
+
+} // anonymous namespace
+
+
+//**************************************************************************
+// DEVICE TYPE DEFINITIONS
+//**************************************************************************
+
+DEFINE_DEVICE_TYPE_PRIVATE(NUBUS_MDC48, device_nubus_card_interface, nubus_48gc_device, "nb_mdc48", "Apple Macintosh Display Card 4/8 (MDC 1.0.1)")
+DEFINE_DEVICE_TYPE_PRIVATE(NUBUS_MDC824, device_nubus_card_interface, nubus_824gc_device, "nb_mdc824", "Apple Macintosh Display Card 8/24 (MDC 1.2)")