diff options
Diffstat (limited to 'src/mame/video/x68k_crtc.cpp')
-rw-r--r-- | src/mame/video/x68k_crtc.cpp | 642 |
1 files changed, 642 insertions, 0 deletions
diff --git a/src/mame/video/x68k_crtc.cpp b/src/mame/video/x68k_crtc.cpp new file mode 100644 index 00000000000..d997b7aefe0 --- /dev/null +++ b/src/mame/video/x68k_crtc.cpp @@ -0,0 +1,642 @@ +// license:BSD-3-Clause +// copyright-holders:Barry Rodewald,Carl + +#include "emu.h" +#include "video/x68k_crtc.h" +#include "screen.h" + +//#define VERBOSE 0 +#include "logmacro.h" + +// device type definitions +DEFINE_DEVICE_TYPE(VINAS, vinas_device, "vinas", "IX0902/IX0903 VINAS CRTC") +DEFINE_DEVICE_TYPE(VICON, vicon_device, "vicon", "IX1093 VICON CRTC") + +x68k_crtc_device::x68k_crtc_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock) + : device_t(mconfig, type, tag, owner, clock) + , device_video_interface(mconfig, *this) + , m_vdisp_callback(*this) + , m_rint_callback(*this) + , m_hsync_callback(*this) + , m_tvram_read_callback(*this) + , m_gvram_read_callback(*this) + , m_tvram_write_callback(*this) + , m_gvram_write_callback(*this) + , m_operation(0) + , m_vblank(false) + , m_hblank(false) + , m_htotal(0) + , m_vtotal(0) + , m_hend(0) + , m_vend(0) + , m_hsync_end(0) + , m_vsync_end(0) + , m_hsyncadjust(0) + , m_vmultiple(1) + , m_height(0) + , m_width(0) + , m_visible_height(0) + , m_visible_width(0) + , m_hshift(0) + , m_vshift(0) + , m_interlace(false) + , m_oddscanline(false) +{ + std::fill(std::begin(m_reg), std::end(m_reg), 0); +} + +vinas_device::vinas_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) + : x68k_crtc_device(mconfig, VINAS, tag, owner, clock) +{ +} + +vicon_device::vicon_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) + : x68k_crtc_device(mconfig, VICON, tag, owner, clock) +{ +} + +void x68k_crtc_device::device_resolve_objects() +{ + m_vdisp_callback.resolve_safe(); + m_rint_callback.resolve_safe(); + m_hsync_callback.resolve_safe(); + m_tvram_read_callback.resolve_safe(0); + m_gvram_read_callback.resolve_safe(0); + m_tvram_write_callback.resolve_safe(); + m_gvram_write_callback.resolve_safe(); +} + +void x68k_crtc_device::device_start() +{ + m_scanline_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(x68k_crtc_device::hsync), this)); + m_operation_end_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(x68k_crtc_device::operation_end), this)); + m_raster_end_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(x68k_crtc_device::raster_end), this)); + m_raster_irq_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(x68k_crtc_device::raster_irq), this)); + m_vblank_irq_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(x68k_crtc_device::vblank_irq), this)); + + // save state + save_item(NAME(m_reg)); + save_item(NAME(m_operation)); + save_item(NAME(m_vblank)); + save_item(NAME(m_hblank)); + save_item(NAME(m_htotal)); + save_item(NAME(m_vtotal)); + save_item(NAME(m_hend)); + save_item(NAME(m_vend)); + save_item(NAME(m_hsync_end)); + save_item(NAME(m_vsync_end)); + save_item(NAME(m_hsyncadjust)); + save_item(NAME(m_vmultiple)); + save_item(NAME(m_height)); + save_item(NAME(m_width)); + save_item(NAME(m_visible_height)); + save_item(NAME(m_visible_width)); + save_item(NAME(m_hshift)); + save_item(NAME(m_vshift)); + save_item(NAME(m_interlace)); + save_item(NAME(m_oddscanline)); +} + +void x68k_crtc_device::device_reset() +{ + // initialise CRTC, set registers to defaults for the standard text mode (768x512) + m_reg[0] = 137; // Horizontal total (in characters) + m_reg[1] = 14; // Horizontal sync end + m_reg[2] = 28; // Horizontal start + m_reg[3] = 124; // Horizontal end + m_reg[4] = 567; // Vertical total + m_reg[5] = 5; // Vertical sync end + m_reg[6] = 40; // Vertical start + m_reg[7] = 552; // Vertical end + m_reg[8] = 27; // Horizontal adjust + + //m_scanline = screen().vpos();// = m_reg[6]; // Vertical start + + // start VBlank timer + m_vblank = true; + attotime const irq_time = screen().time_until_pos(m_reg[6],2); + m_vblank_irq_timer->adjust(irq_time); + + // start HBlank timer + m_scanline_timer->adjust(screen().scan_period(), 1); + + m_vdisp_callback(1); + m_rint_callback(1); + m_hsync_callback(1); +} + +void x68k_crtc_device::text_copy(unsigned src, unsigned dest, u8 planes) +{ + // copys one raster in T-VRAM to another raster + offs_t src_ram = src * 256; // 128 bytes per scanline + offs_t dest_ram = dest * 256; + + // update RAM in each plane + for (int words = 256; words > 0; words--, src_ram++, dest_ram++) + { + for (u8 plane = 0; plane < 3; plane++) + if (BIT(planes, plane)) + m_tvram_write_callback(dest_ram + 0x10000 * plane, m_tvram_read_callback(src_ram + 0x10000 * plane, 0xffff), 0xffff); + } +} + +TIMER_CALLBACK_MEMBER(x68k_crtc_device::operation_end) +{ + int bit = param; + m_operation &= ~bit; +} + +void x68k_crtc_device::refresh_mode() +{ + // Calculate data from register values + m_vmultiple = 1; + if ((m_reg[20] & 0x10) != 0 && (m_reg[20] & 0x0c) == 0) + m_vmultiple = 2; // 31.5kHz + 256 lines = doublescan + if (m_interlace) + m_vmultiple = 0.5f; // 31.5kHz + 1024 lines or 15kHz + 512 lines = interlaced + m_htotal = (m_reg[0] + 1) * 8; + m_vtotal = (m_reg[4] + 1) / m_vmultiple; // default is 567 (568 scanlines) + m_hbegin = (m_reg[2] * 8) + 1; + m_hend = (m_reg[3] * 8); + m_vbegin = (m_reg[6]) / m_vmultiple; + m_vend = (m_reg[7] - 1) / m_vmultiple; + if ((m_vmultiple == 2) && !(m_reg[7] & 1)) // otherwise if the raster irq line == vblank line, the raster irq fires too late + m_vend++; + m_hsync_end = (m_reg[1]) * 8; + m_vsync_end = (m_reg[5]) / m_vmultiple; + m_hsyncadjust = m_reg[8]; + + rectangle scr(0, m_htotal - 8, 0, m_vtotal); + if (scr.max_y <= m_vend) + scr.max_y = m_vend + 2; + if (scr.max_x <= m_hend) + scr.max_x = m_hend + 2; + rectangle visiblescr(m_hbegin, m_hend, m_vbegin, m_vend); + + // expand visible area to the size indicated by CRTC reg 20 + int length = m_hend - m_hbegin; + if (length < m_width) + { + visiblescr.min_x = m_hbegin - ((m_width - length)/2); + visiblescr.max_x = m_hend + ((m_width - length)/2); + } + length = m_vend - m_vbegin; + if (length < m_height) + { + visiblescr.min_y = m_vbegin - ((m_height - length)/2); + visiblescr.max_y = m_vend + ((m_height - length)/2); + } + // bounds check + if (visiblescr.min_x < 0) + visiblescr.min_x = 0; + if (visiblescr.min_y < 0) + visiblescr.min_y = 0; + if (visiblescr.max_x >= scr.max_x) + visiblescr.max_x = scr.max_x - 2; + if (visiblescr.max_y >= scr.max_y - 1) + visiblescr.max_y = scr.max_y - 2; + +// LOG("CRTC regs - %i %i %i %i - %i %i %i %i - %i - %i\n", m_reg[0], m_reg[1], m_reg[2], m_reg[3], +// m_reg[4], m_reg[5], m_reg[6], m_reg[7], m_reg[8], m_reg[9]); + unsigned div = (m_reg[20] & 0x03) == 0 ? 4 : 2; + if (BIT(m_reg[20], 4) && !BIT(m_reg[20], 1)) + div = BIT(m_reg[20], 0) ? 3 : 6; + if ((m_reg[20] & 0x0c) == 0) + div *= 2; + attotime refresh = attotime::from_ticks(scr.max_x * scr.max_y, (BIT(m_reg[20], 4) ? 69.55199_MHz_XTAL : 38.86363_MHz_XTAL) / div); + LOG("screen().configure(%i,%i,[%i,%i,%i,%i],%f)\n", scr.max_x, scr.max_y, visiblescr.min_x, visiblescr.min_y, visiblescr.max_x, visiblescr.max_y, ATTOSECONDS_TO_HZ(refresh.as_attoseconds())); + screen().configure(scr.max_x, scr.max_y, visiblescr, refresh.as_attoseconds()); +} + +TIMER_CALLBACK_MEMBER(x68k_crtc_device::hsync) +{ + int hstate = param; + attotime hsync_time; + + m_hblank = hstate; + m_hsync_callback(!m_hblank); + + if (m_operation & 8) + text_copy((m_reg[22] & 0xff00) >> 8, (m_reg[22] & 0x00ff), (m_reg[21] & 0xf)); + + if (m_vmultiple == 2) // 256-line (doublescan) + { + if (hstate == 1) + { + if (m_oddscanline) + { + int scan = screen().vpos(); + if (scan > m_vend) + scan = m_vbegin; + hsync_time = screen().time_until_pos(scan, (m_htotal + m_hend) / 2); + m_scanline_timer->adjust(hsync_time); + if (scan != 0) + screen().update_partial(scan); + } + else + { + int scan = screen().vpos(); + if (scan > m_vend) + scan = m_vbegin; + hsync_time = screen().time_until_pos(scan, m_hend / 2); + m_scanline_timer->adjust(hsync_time); + if (scan != 0) + screen().update_partial(scan); + } + } + if (hstate == 0) + { + if (m_oddscanline) + { + int scan = screen().vpos(); + if (scan > m_vend) + scan = m_vbegin; + else + scan++; + hsync_time = screen().time_until_pos(scan, m_hbegin / 2); + m_scanline_timer->adjust(hsync_time, 1); + m_oddscanline = false; + } + else + { + hsync_time = screen().time_until_pos(screen().vpos(), (m_htotal + m_hbegin) / 2); + m_scanline_timer->adjust(hsync_time, 1); + m_oddscanline = true; + } + } + } + else // 512-line + { + if (hstate == 1) + { + int scan = screen().vpos(); + if (scan > m_vend) + scan = 0; + hsync_time = screen().time_until_pos(scan, m_hend); + m_scanline_timer->adjust(hsync_time); + if (scan != 0) + screen().update_partial(scan); + } + if (hstate == 0) + { + hsync_time = screen().time_until_pos(screen().vpos() + 1, m_hbegin); + m_scanline_timer->adjust(hsync_time, 1); + } + } +} + +TIMER_CALLBACK_MEMBER(x68k_crtc_device::raster_end) +{ + m_rint_callback(1); +} + +TIMER_CALLBACK_MEMBER(x68k_crtc_device::raster_irq) +{ + int scan = param; + attotime irq_time; + attotime end_time; + + if (scan <= m_vtotal) + { + m_rint_callback(0); + screen().update_partial(scan); + irq_time = screen().time_until_pos(scan, m_hbegin); + // end of HBlank period clears GPIP6 also? + end_time = screen().time_until_pos(scan, m_hend); + m_raster_irq_timer->adjust(irq_time, scan); + m_raster_end_timer->adjust(end_time); + LOG("GPIP6: Raster triggered at line %i (%i)\n", scan,screen().vpos()); + } +} + +TIMER_CALLBACK_MEMBER(x68k_crtc_device::vblank_irq) +{ + int val = param; + attotime irq_time; + int vblank_line; + + if (val == 1) // V-DISP on + { + m_vblank = 1; + vblank_line = m_vbegin; + irq_time = screen().time_until_pos(vblank_line, 2); + m_vblank_irq_timer->adjust(irq_time); + LOG("CRTC: VBlank on\n"); + } + if (val == 0) // V-DISP off + { + m_vblank = 0; + vblank_line = m_vend; + if (vblank_line > m_vtotal) + vblank_line = m_vtotal; + irq_time = screen().time_until_pos(vblank_line, 2); + m_vblank_irq_timer->adjust(irq_time, 1); + LOG("CRTC: VBlank off\n"); + } + + m_vdisp_callback(!m_vblank); +} + + +// CRTC "VINAS 1+2 / VICON" at 0xe80000 +/* 0xe80000 - Registers (all are 16-bit): + * 0 - Horizontal Total (in characters) + * 1 - Horizontal Sync End + * 2 - Horizontal Display Begin + * 3 - Horizontal Display End + * 4 - Vertical Total (in scanlines) + * 5 - Vertical Sync End + * 6 - Vertical Display Begin + * 7 - Vertical Display End + * 8 - Fine Horizontal Sync Adjustment + * 9 - Raster Line (for Raster IRQ mapped to MFP GPIP6) + * 10/11 - Text Layer X and Y Scroll + * 12/13 - Graphic Layer 0 X and Y Scroll + * 14/15 - Graphic Layer 1 X and Y Scroll + * 16/17 - Graphic Layer 2 X and Y Scroll + * 18/19 - Graphic Layer 3 X and Y Scroll + * 20 - bit 12 - Text VRAM mode : 0 = display, 1 = buffer + * bit 11 - Graphic VRAM mode : 0 = display, 1 = buffer + * bit 10 - "Real" screen size : 0 = 512x512, 1 = 1024x1024 + * bits 8,9 - Colour mode : + * 00 = 16 colour 01 = 256 colour + * 10 = Undefined 11 = 65,536 colour + * bit 4 - Horizontal Frequency : 0 = 15.98kHz, 1 = 31.50kHz + * bits 2,3 - Vertical dots : + * 00 = 256 01 = 512 + * 10 or 11 = 1024 (interlaced) + * bits 0,1 - Horizontal dots : + * 00 = 256 01 = 512 + * 10 = 768 11 = 50MHz clock mode (Compact XVI or later) + * 21 - bit 9 - Text Screen Access Mask Enable + * bit 8 - Text Screen Simultaneous Plane Access Enable + * bits 4-7 - Text Screen Simultaneous Plane Access Select + * bits 0-3 - Text Screen Line Copy Plane Select + * Graphic Screen High-speed Clear Page Select + * 22 - Text Screen Line Copy + * bits 15-8 - Source Line + * bits 7-0 - Destination Line + * 23 - Text Screen Mask Pattern + * + * 0xe80481 - Operation Port (8-bit): + * bit 3 - Text Screen Line Copy Begin + * bit 1 - Graphic Screen High-speed Clear Begin + * bit 0 - Image Taking Begin (?) + * Operation Port bits are cleared automatically when the requested + * operation is completed. + */ +WRITE16_MEMBER(x68k_crtc_device::crtc_w) +{ + if (offset < 0x24) + COMBINE_DATA(&m_reg[offset]); + switch (offset) + { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + refresh_mode(); + break; + case 9: // CRTC raster IRQ (GPIP6) + { + data = m_reg[9]; + attotime irq_time = screen().time_until_pos((data) / m_vmultiple,2); + + if (data != screen().vpos()) + m_rint_callback(1); + if (irq_time.as_double() > 0) + m_raster_irq_timer->adjust(irq_time, (data) / m_vmultiple); + } + LOG("CRTC: Write to raster IRQ register - %i\n",data); + break; + case 20: + if (ACCESSING_BITS_0_7) + { + m_interlace = false; + switch (data & 0x0c) + { + case 0x00: + m_height = 256; + break; + case 0x08: + case 0x0c: // TODO: 1024 vertical, if horizontal freq = 31kHz + m_height = 512; + m_interlace = true; // if 31kHz, 1024 lines = interlaced + break; + case 0x04: + m_height = 512; + if (!(m_reg[20] & 0x0010)) // if 15kHz, 512 lines = interlaced + m_interlace = true; + break; + } + switch (data & 0x03) + { + case 0x00: + m_width = 256; + break; + case 0x01: + m_width = 512; + break; + case 0x02: + case 0x03: // 0x03 = 50MHz clock mode (XVI only) + m_width = 768; + break; + } + } +/* if (ACCESSING_BITS_8_15) + { + m_interlace = false; + if (data & 0x0400) + m_interlace = true; + }*/ + logerror("CRTC: Register 20 = %04x\n", m_reg[20]); + refresh_mode(); + break; + case 576: // operation register + m_operation = data; + if (data & 0x02) // high-speed graphic screen clear + { + for (offs_t addr = 0; addr < 0x40000; addr++) + m_gvram_write_callback(addr, 0, 0xffff); + m_operation_end_timer->adjust(attotime::from_msec(10), 0x02); // time taken to do operation is a complete guess. + } + break; + } +// LOG("%s CRTC: Wrote %04x to CRTC register %i\n",machine().describe_context(), data, offset); +} + +READ16_MEMBER(x68k_crtc_device::crtc_r) +{ + if (offset < 24) + { +// LOG("%s CRTC: Read %04x from CRTC register %i\n",machine().describe_context(), m_reg[offset], offset); + switch (offset) + { + case 9: + return 0; + case 10: // Text X/Y scroll + case 11: + case 12: // Graphic layer 0 scroll + case 13: + return m_reg[offset] & 0x3ff; + case 14: // Graphic layer 1 scroll + case 15: + case 16: // Graphic layer 2 scroll + case 17: + case 18: // Graphic layer 3 scroll + case 19: + return m_reg[offset] & 0x1ff; + default: + return m_reg[offset]; + } + } + if (offset == 576) // operation port, operation bits are set to 0 when operation is complete + return m_operation; +// LOG("CRTC: [%08x] Read from unknown CRTC register %i\n",activecpu_get_pc(),offset); + return 0xffff; +} + +WRITE16_MEMBER(x68k_crtc_device::gvram_w) +{ +// int xloc,yloc,pageoffset; + /* + G-VRAM usage is determined by colour depth and "real" screen size. + + For screen size of 1024x1024, all G-VRAM space is used, in one big page. + At 1024x1024 real screen size, colour depth is always 4bpp, and ranges from + 0xc00000-0xdfffff. + + For screen size of 512x512, the colour depth determines the page usage. + 16 colours = 4 pages + 256 colours = 2 pages + 65,536 colours = 1 page + Page 1 - 0xc00000-0xc7ffff Page 2 - 0xc80000-0xcfffff + Page 3 - 0xd00000-0xd7ffff Page 4 - 0xd80000-0xdfffff + */ + + // handle different G-VRAM page setups + if (m_reg[20] & 0x0800) // G-VRAM set to buffer + { + if (offset < 0x40000) + m_gvram_write_callback(offset, data, mem_mask); + } + else + { + switch (m_reg[20] & 0x0300) + { + case 0x0300: + if (offset < 0x40000) + m_gvram_write_callback(offset, data, mem_mask); + break; + case 0x0100: + if (offset < 0x40000) + { + m_gvram_write_callback(offset, data & 0x00ff, 0x00ff); + } + else if (offset >= 0x40000 && offset < 0x80000) + { + m_gvram_write_callback(offset - 0x40000, (data & 0x00ff) << 8, 0xff00); + } + break; + case 0x0000: + if (offset < 0x40000) + { + m_gvram_write_callback(offset, data & 0x000f, 0x000f); + } + else if (offset >= 0x40000 && offset < 0x80000) + { + m_gvram_write_callback(offset - 0x40000, (data & 0x000f) << 4, 0x00f0); + } + else if (offset >= 0x80000 && offset < 0xc0000) + { + m_gvram_write_callback(offset - 0x80000, (data & 0x000f) << 8, 0x0f00); + } + else if (offset >= 0xc0000 && offset < 0x100000) + { + m_gvram_write_callback(offset - 0xc0000, (data & 0x000f) << 12, 0xf000); + } + break; + default: + logerror("G-VRAM written while layer setup is undefined.\n"); + } + } +} + +WRITE16_MEMBER(x68k_crtc_device::tvram_w) +{ + u16 text_mask = ~(m_reg[23]) & mem_mask; + + if (!(m_reg[21] & 0x0200)) // text access mask enable + text_mask = 0xffff & mem_mask; + + mem_mask = text_mask; + + if (m_reg[21] & 0x0100) + { + // simultaneous T-VRAM plane access (I think ;)) + offset &= 0x00ffff; + u8 wr = (m_reg[21] & 0x00f0) >> 4; + for (int plane = 0; plane < 4; plane++) + { + if (BIT(wr, plane)) + { + m_tvram_write_callback(offset + 0x10000 * plane, data, mem_mask); + } + } + } + else + { + m_tvram_write_callback(offset, data, mem_mask); + } +} + +READ16_MEMBER(x68k_crtc_device::gvram_r) +{ + u16 ret = 0; + + if (m_reg[20] & 0x0800) // G-VRAM set to buffer + return m_gvram_read_callback(offset); + + switch (m_reg[20] & 0x0300) // colour setup determines G-VRAM use + { + case 0x0300: // 65,536 colour (RGB) - 16-bits per word + if (offset < 0x40000) + ret = m_gvram_read_callback(offset); + else + ret = 0xffff; + break; + case 0x0100: // 256 colour (paletted) - 8 bits per word + if (offset < 0x40000) + ret = m_gvram_read_callback(offset) & 0x00ff; + else if (offset >= 0x40000 && offset < 0x80000) + ret = (m_gvram_read_callback(offset - 0x40000) & 0xff00) >> 8; + else if (offset >= 0x80000) + ret = 0xffff; + break; + case 0x0000: // 16 colour (paletted) - 4 bits per word + if (offset < 0x40000) + ret = m_gvram_read_callback(offset) & 0x000f; + else if (offset >= 0x40000 && offset < 0x80000) + ret = (m_gvram_read_callback(offset - 0x40000) & 0x00f0) >> 4; + else if (offset >= 0x80000 && offset < 0xc0000) + ret = (m_gvram_read_callback(offset - 0x80000) & 0x0f00) >> 8; + else if (offset >= 0xc0000 && offset < 0x100000) + ret = (m_gvram_read_callback(offset - 0xc0000) & 0xf000) >> 12; + break; + default: + logerror("G-VRAM read while layer setup is undefined.\n"); + ret = 0xffff; + } + + return ret; +} + +READ16_MEMBER(x68k_crtc_device::tvram_r) +{ + return m_tvram_read_callback(offset, mem_mask); +} |