diff options
Diffstat (limited to 'src/devices/machine/mv_sonora.cpp')
-rw-r--r-- | src/devices/machine/mv_sonora.cpp | 405 |
1 files changed, 405 insertions, 0 deletions
diff --git a/src/devices/machine/mv_sonora.cpp b/src/devices/machine/mv_sonora.cpp new file mode 100644 index 00000000000..096257c4257 --- /dev/null +++ b/src/devices/machine/mv_sonora.cpp @@ -0,0 +1,405 @@ +// license:BSD-3-Clause +// copyright-holders:Olivier Galibert +/********************************************************************* + + Mac video support, "Sonora" edition + Supports 5 different modelines at up to 16bpp + + The original Sonora ASIC and its follow-on Ardbeg require an + external pixel clock source, while the version used in the PDM + machines has an internal clock source that automatically is set + appropriately for the video mode. + +*********************************************************************/ + +#include "emu.h" +#include "mv_sonora.h" + +DEFINE_DEVICE_TYPE(MAC_VIDEO_SONORA, mac_video_sonora_device, "mv_sonora", "Mac Sonora video support") + +const mac_video_sonora_device::modeline mac_video_sonora_device::modelines[5] = { + { 0x02, "512x384 12\" RGB", 15667200, 640, 16, 32, 80, 407, 1, 3, 19, true, false }, + { 0x06, "640x480 13\" RGB", 31334400, 896, 80, 64, 112, 525, 3, 3, 39, true, false }, + { 0x01, "640x870 15\" Portrait", 57283200, 832, 32, 80, 80, 918, 3, 3, 42, false, true }, + { 0x09, "832x624 16\" RGB", 57283200, 1152, 32, 64, 224, 667, 1, 3, 39, false, false }, + { 0x0b, "640x480 VGA", 25175000, 800, 16, 96, 48, 525, 10, 2, 33, false, false }, +}; + +mac_video_sonora_device::mac_video_sonora_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : + device_t(mconfig, MAC_VIDEO_SONORA, tag, owner, clock), + m_screen(*this, "screen"), + m_palette(*this, "palette"), + m_monitor_config(*this, "monitor"), + m_screen_vblank(*this), + m_isPDM(false), + m_is32bit(false) +{ +} + +void mac_video_sonora_device::device_start() +{ + m_vram = nullptr; + + save_item(NAME(m_vram_offset)); + save_item(NAME(m_mode)); + save_item(NAME(m_depth)); + save_item(NAME(m_monitor_id)); + save_item(NAME(m_vtest)); + save_item(NAME(m_modeline_id)); +} + +void mac_video_sonora_device::device_reset() +{ + m_modeline_id = -1; + m_mode = 0x9f; + m_depth = 0; + m_monitor_id = 8; + m_vtest = 0; + m_vram_offset = 0; +} + +void mac_video_sonora_device::device_add_mconfig(machine_config &config) +{ + SCREEN(config, m_screen, SCREEN_TYPE_RASTER); + // dot clock, htotal, hstart, hend, vtotal, vstart, vend + m_screen->set_raw(31334400, 896, 0, 640, 525, 0, 480); + m_screen->set_screen_update(FUNC(mac_video_sonora_device::screen_update)); + m_screen->screen_vblank().set([this](int state) { m_screen_vblank(state && (m_modeline_id != -1)); }); + + PALETTE(config, m_palette).set_entries(256); +} + +// The monitor detection goes through 3 sense lines which are +// pulled-up but can be set to 0 or 1 by the system. + +// The monitor itself either connects some lines to ground (old +// method) or connects some lines together, sometimes with a diode +// (extended monitor sense). + +// The system starts by leaving everything undriven and reading the +// result. If it's not 7, it's the monitor type. If it's 7, then it +// needs the extended monitor sense. It then drives a 0 on each of +// the 3 pins in sequence and records the result on the other two, and +// that gives the signature. + +static constexpr uint8_t ext(uint8_t bc, uint8_t ac, uint8_t ab) +{ + return 0x40 | (bc << 4) | (ac << 2) | ab; +} + +static INPUT_PORTS_START(monitor_config) + PORT_START("monitor") + PORT_CONFNAME(0x7f, 6, "Monitor type") + PORT_CONFSETTING(2, "512x384 12\" RGB") + PORT_CONFSETTING(6, "640x480 13\" RGB") // Biggest resolution with 16bpp support, hence default + PORT_CONFSETTING(1, "620x870 15\" Portrait") + PORT_CONFSETTING(ext(2, 3, 1), "832x624 16\" RGB") + PORT_CONFSETTING(ext(1, 1, 3), "640x480 VGA") +INPUT_PORTS_END + + +ioport_constructor mac_video_sonora_device::device_input_ports() const +{ + return INPUT_PORTS_NAME(monitor_config); +} + +uint32_t mac_video_sonora_device::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect) +{ + if((m_mode & 0x80) || m_modeline_id == -1 || m_depth > 4 || (m_depth == 4 && !modelines[m_modeline_id].supports_16bpp) || !m_vram) { + bitmap.fill(0); + return 0; + } + + const auto &m = modelines[m_modeline_id]; + uint32_t hres = m.htot - m.hfp - m.hs - m.hbp; + uint32_t vres = m.vtot - m.vfp - m.vs - m.vbp; + + const pen_t *pens = m_palette->pens(); + + if (m_is32bit) { + const uint32_t *vram = (uint32_t *)(m_vram + (m_vram_offset / 8)); + switch (m_depth) + { + case 0: // 1bpp + for (uint32_t y = 0; y != vres; y++) + { + uint32_t *scanline = &bitmap.pix(y); + for (uint32_t x = 0; x != hres; x += 32) + { + uint32_t pixels = *vram++; + for (int32_t bit = 31; bit >= 0; bit--) + *scanline++ = pens[(((pixels >> bit) & 1) << 7) | 0x7f]; + } + } + break; + + case 1: // 2bpp + for (uint32_t y = 0; y != vres; y++) + { + uint32_t *scanline = &bitmap.pix(y); + for (uint32_t x = 0; x != hres; x += 16) + { + uint32_t pixels = *vram++; + for (int32_t bit = 30; bit >= 0; bit -= 2) + *scanline++ = pens[(((pixels >> bit) & 0x3)<<6) | 0x3f]; + } + } + break; + + case 2: // 4bpp + for (uint32_t y = 0; y != vres; y++) + { + uint32_t *scanline = &bitmap.pix(y); + for (uint32_t x = 0; x != hres; x += 8) + { + uint32_t pixels = *vram++; + for (int32_t bit = 28; bit >= 0; bit -= 4) + *scanline++ = pens[(((pixels >> bit) & 0x0f)<<4) | 0x0f]; + } + } + break; + + case 3: // 8bpp + for (uint32_t y = 0; y != vres; y++) + { + uint32_t *scanline = &bitmap.pix(y); + for (uint32_t x = 0; x != hres; x += 4) + { + uint32_t pixels = *vram++; + for (int32_t bit = 24; bit >= 0; bit -= 8) + *scanline++ = pens[((pixels >> bit) & 0xff)]; + } + } + break; + + case 4: // 16bpp + for (uint32_t y = 0; y != vres; y++) + { + uint32_t *scanline = &bitmap.pix(y); + for (uint32_t x = 0; x != hres; x += 2) + { + const uint32_t pixels = *vram++; + *scanline++ = rgb_t(((pixels >> 26) & 0x1f) << 3, ((pixels >> 21) & 0x1f) << 3, ((pixels >> 16) & 0x1f) << 3); + *scanline++ = rgb_t(((pixels >> 10) & 0x1f) << 3, ((pixels >> 5) & 0x1f) << 3, (pixels & 0x1f) << 3); + } + } + break; + + default: + bitmap.fill(0xff0000); + break; + } + } else { + const uint64_t *vram = m_vram + (m_vram_offset / 8); + switch(m_depth) { + case 0: // 1bpp + for(uint32_t y = 0; y != vres; y++) { + uint32_t *scanline = &bitmap.pix(y); + for(uint32_t x = 0; x != hres; x += 64) { + uint64_t pixels = *vram ++; + for(int32_t bit = 63; bit >= 0; bit --) + *scanline ++ = pens[(((pixels >> bit) & 1) << 7) | 0x7f]; + } + } + break; + + case 1: // 2bpp + for (uint32_t y = 0; y != vres; y++) + { + uint32_t *scanline = &bitmap.pix(y); + for (uint32_t x = 0; x != hres; x += 32) + { + uint64_t pixels = *vram++; + for (int32_t bit = 62; bit >= 0; bit -= 2) + *scanline ++ = pens[(((pixels >> bit) & 0x3) << 6) | 0x3f]; + } + } + break; + + case 2: // 4bpp + for (uint32_t y = 0; y != vres; y++) + { + uint32_t *scanline = &bitmap.pix(y); + for (uint32_t x = 0; x != hres; x += 16) + { + uint64_t pixels = *vram++; + for (int32_t bit = 60; bit >= 0; bit -= 4) + *scanline ++ = pens[(((pixels >> bit) & 0x0f) << 4) | 0x0f]; + } + } + break; + + case 3: // 8bpp + for(uint32_t y = 0; y != vres; y++) { + uint32_t *scanline = &bitmap.pix(y); + for(uint32_t x = 0; x != hres; x += 8) { + uint64_t pixels = *vram ++; + for(int32_t bit = 56; bit >= 0; bit -= 8) + *scanline ++ = pens[((pixels >> bit) & 0xff)]; + } + } + break; + + case 4: // 16bpp + for (uint32_t y = 0; y != vres; y++) + { + uint32_t *scanline = &bitmap.pix(y); + for (uint32_t x = 0; x != hres; x += 4) + { + const uint64_t pixels = *vram++; + *scanline++ = rgb_t(((pixels >> 58) & 0x1f) << 3, ((pixels >> 53) & 0x1f) << 3, ((pixels >> 48) & 0x1f) << 3); + *scanline++ = rgb_t(((pixels >> 42) & 0x1f) << 3, ((pixels >> 37) & 0x1f) << 3, ((pixels >> 32) & 0x1f) << 3); + *scanline++ = rgb_t(((pixels >> 26) & 0x1f) << 3, ((pixels >> 21) & 0x1f) << 3, ((pixels >> 16) & 0x1f) << 3); + *scanline++ = rgb_t(((pixels >> 10) & 0x1f) << 3, ((pixels >> 5) & 0x1f) << 3, (pixels & 0x1f) << 3); + } + } + break; + + default: + bitmap.fill(0xff0000); + break; + } + } + + return 0; +} + +uint8_t mac_video_sonora_device::vctrl_r(offs_t offset) +{ + switch(offset) { + case 0: return m_mode; + case 1: return m_depth; + + case 2: { + uint8_t mon = m_monitor_config->read(); + uint8_t res; + if(mon & 0x40) { + res = 7; + if(!(m_monitor_id & 0xc)) + res &= 4 | (BIT(mon, 5) << 1) | BIT(mon, 4); + if(!(m_monitor_id & 0xa)) + res &= (BIT(mon, 3) << 2) | 2 | BIT(mon, 2); + if(!(m_monitor_id & 0x9)) + res &= (BIT(mon, 1) << 2) | (BIT(mon, 0) << 1) | 1; + + } else { + res = mon; + if(!(m_monitor_id & 8)) + res &= m_monitor_id & 7; + } + + return m_monitor_id | (res << 4); + } + + case 3: return m_vtest; + case 4: return (m_screen->hpos() >> 8) & 7; + case 5: return m_screen->hpos() & 0xff; + case 6: return (m_screen->vpos() >> 8) & 3; + case 7: return m_screen->vpos() & 0xff; + } + + return 0; +} + +void mac_video_sonora_device::vctrl_w(offs_t offset, uint8_t data) +{ + switch(offset) { + case 0: { + int prev_modeline = m_modeline_id; + m_mode = data & 0x9f; + m_modeline_id = -1; + for(unsigned i=0; i != std::size(modelines) && m_modeline_id == -1; i++) + if((m_mode & 0x1f) == modelines[i].mode_id) + m_modeline_id = i; + + logerror("Mode switch %02x %s%s\n", data, + m_mode & 0x80 ? "blanked " : "", + m_modeline_id == -1 ? "disabled" : modelines[m_modeline_id].name); + + if(m_modeline_id != -1 && m_modeline_id != prev_modeline) { + const modeline &m = modelines[m_modeline_id]; + rectangle visarea(0, m.htot - m.hfp - m.hs - m.hbp - 1, 0, m.vtot - m.vfp - m.vs - m.vbp - 1); + if (m_isPDM) { + m_screen->configure(m.htot, m.vtot, visarea, attotime::from_ticks(m.htot*m.vtot, m.dotclock).as_attoseconds()); + } else { + m_screen->configure(m.htot, m.vtot, visarea, attotime::from_ticks(m.htot * m.vtot, m_extPixelClock).as_attoseconds()); + } + } + break; + } + + case 1: + m_depth = data & 7; + if(m_depth <= 4) + logerror("Pixel depth %dbpp\n", 1 << m_depth); + else + logerror("Pixel depth invalid (%d)\n", m_depth); + break; + + case 2: + m_monitor_id = data & 0xf; + break; + + case 3: + m_vtest = data & 1; + break; + + default: + logerror("vctrl_w %x, %02x\n", offset, data); + } +} + +uint8_t mac_video_sonora_device::dac_r(offs_t offset) +{ + switch(offset) { + case 2: + return m_pal_control; + + default: +// logerror("dac_r %x\n", offset); + return 0; + } +} + +void mac_video_sonora_device::dac_w(offs_t offset, uint8_t data) +{ + switch(offset) { + case 0: + m_pal_address = data; + m_pal_idx = 0; + break; + + case 1: + switch(m_pal_idx) { + case 0: m_palette->set_pen_red_level(m_pal_address, data); break; + case 1: m_palette->set_pen_green_level(m_pal_address, data); break; + case 2: + // monochrome monitors use the blue line as the video, so duplicate blue to all 3 primaries + if (modelines[m_modeline_id].monochrome) { + m_palette->set_pen_red_level(m_pal_address, data); + m_palette->set_pen_green_level(m_pal_address, data); + m_palette->set_pen_blue_level(m_pal_address, data); + } + else + { + m_palette->set_pen_blue_level(m_pal_address, data); + } + break; + } + m_pal_idx ++; + if(m_pal_idx == 3) { + m_pal_idx = 0; + m_pal_address ++; + } + break; + + case 2: + logerror("control = %02x\n", data); + m_pal_control = data; + break; + + case 3: + m_pal_colkey = data; + break; + } +} |