From f5b2fd9ee6ef6ced1eec7859494e82c375d9c8d2 Mon Sep 17 00:00:00 2001 From: Angelo Salese Date: Sat, 24 Aug 2019 11:33:39 +0200 Subject: Major refactoring of VRender0 SoC device [Angelo Salese] (#5527) * Improved encapsulation between video and machine SoC periperals; * Split up HWs in individual files where they don't belong to Crystal System HW, makes future development easier; * Untangled reads/writes to draw/display bankswitches from screen_update, now they can be unthrottled safely; * Added CRTC screen raw parameters; * Add DMA hold feature and clear irq on mask writes, specific for P's Attack; * Improved Cross Puzzle flash loading, currently failing at POST for a SPU error; nexus3d.cpp: add some preliminary work, currently does some VRender3d pipeline fill with a debug trick [Angelo Salese] (out of whatsnew) Some stuff definitely needs fine graining, like removing the few lines that are still necessary to configure the VRender0 from driver files, which I'm gonna do in my next feature branch. --- src/devices/machine/vrender0.cpp | 576 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 576 insertions(+) create mode 100644 src/devices/machine/vrender0.cpp (limited to 'src/devices/machine/vrender0.cpp') diff --git a/src/devices/machine/vrender0.cpp b/src/devices/machine/vrender0.cpp new file mode 100644 index 00000000000..daf3a7f3f64 --- /dev/null +++ b/src/devices/machine/vrender0.cpp @@ -0,0 +1,576 @@ +// license:BSD-3-Clause +// copyright-holders:Angelo Salese, ElSemi +/*************************************************************************** + + MagicEyes VRender0 SoC peripherals + + Device by Angelo Salese + Based off original crystal.cpp by ElSemi + + TODO: + - Improve encapsulation, still needs a few trampolines from host driver; + - Proper PIO emulation; + - Output CRTC border color; + - Add VCLK select; + +***************************************************************************/ + +#include "emu.h" +#include "vrender0.h" + + + +//************************************************************************** +// GLOBAL VARIABLES +//************************************************************************** + +// device type definition +DEFINE_DEVICE_TYPE(VRENDER0_SOC, vrender0soc_device, "vrender0", "MagicEyes VRender0 SoC") + + +//************************************************************************** +// LIVE DEVICE +//************************************************************************** + +//------------------------------------------------- +// vrender0soc_device - constructor +//------------------------------------------------- + +vrender0soc_device::vrender0soc_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : device_t(mconfig, VRENDER0_SOC, tag, owner, clock), + m_host_cpu(*this, finder_base::DUMMY_TAG), + m_host_screen(*this, finder_base::DUMMY_TAG), + m_crtcregs(*this, "crtcregs"), + m_idleskip_cb(*this) +{ +} + +void vrender0soc_device::regs_map(address_map &map) +{ +// map(0x00000, 0x003ff) // System/General + map(0x00000, 0x00003).r(FUNC(vrender0soc_device::sysid_r)); + map(0x00004, 0x00007).r(FUNC(vrender0soc_device::cfgr_r)); + map(0x00010, 0x00017).noprw(); // watchdog +// map(0x00400, 0x007ff) // Local Memory Controller +// map(0x00800, 0x00bff) // DMA + map(0x00800, 0x00803).rw(FUNC(vrender0soc_device::dmac_r<0>), FUNC(vrender0soc_device::dmac_w<0>)); + map(0x00804, 0x00807).rw(FUNC(vrender0soc_device::dmasa_r<0>), FUNC(vrender0soc_device::dmasa_w<0>)); + map(0x00808, 0x0080b).rw(FUNC(vrender0soc_device::dmada_r<0>), FUNC(vrender0soc_device::dmada_w<0>)); + map(0x0080c, 0x0080f).rw(FUNC(vrender0soc_device::dmatc_r<0>), FUNC(vrender0soc_device::dmatc_w<0>)); + map(0x00810, 0x00813).rw(FUNC(vrender0soc_device::dmac_r<1>), FUNC(vrender0soc_device::dmac_w<1>)); + map(0x00814, 0x00817).rw(FUNC(vrender0soc_device::dmasa_r<1>), FUNC(vrender0soc_device::dmasa_w<1>)); + map(0x00818, 0x0081b).rw(FUNC(vrender0soc_device::dmada_r<1>), FUNC(vrender0soc_device::dmada_w<1>)); + map(0x0081c, 0x0081f).rw(FUNC(vrender0soc_device::dmatc_r<1>), FUNC(vrender0soc_device::dmatc_w<1>)); + +// map(0x00c00, 0x00fff) // Interrupt Controller + map(0x00c04, 0x00c07).rw(FUNC(vrender0soc_device::intvec_r), FUNC(vrender0soc_device::intvec_w)); + map(0x00c08, 0x00c0b).rw(FUNC(vrender0soc_device::inten_r), FUNC(vrender0soc_device::inten_w)); + map(0x00c0c, 0x00c0f).rw(FUNC(vrender0soc_device::intst_r), FUNC(vrender0soc_device::intst_w)); +// map(0x01000, 0x013ff) // UART +// map(0x01400, 0x017ff) // Timer & Counter + map(0x01400, 0x01403).rw(FUNC(vrender0soc_device::tmcon_r<0>), FUNC(vrender0soc_device::tmcon_w<0>)); + map(0x01404, 0x01407).rw(FUNC(vrender0soc_device::tmcnt_r<0>), FUNC(vrender0soc_device::tmcnt_w<0>)).umask32(0x0000ffff); + map(0x01408, 0x0140b).rw(FUNC(vrender0soc_device::tmcon_r<1>), FUNC(vrender0soc_device::tmcon_w<1>)); + map(0x0140c, 0x0140f).rw(FUNC(vrender0soc_device::tmcnt_r<1>), FUNC(vrender0soc_device::tmcnt_w<1>)).umask32(0x0000ffff); + map(0x01410, 0x01413).rw(FUNC(vrender0soc_device::tmcon_r<2>), FUNC(vrender0soc_device::tmcon_w<2>)); + map(0x01414, 0x01417).rw(FUNC(vrender0soc_device::tmcnt_r<2>), FUNC(vrender0soc_device::tmcnt_w<2>)).umask32(0x0000ffff); + map(0x01418, 0x0141b).rw(FUNC(vrender0soc_device::tmcon_r<3>), FUNC(vrender0soc_device::tmcon_w<3>)); + map(0x0141c, 0x0141f).rw(FUNC(vrender0soc_device::tmcnt_r<3>), FUNC(vrender0soc_device::tmcnt_w<3>)).umask32(0x0000ffff); + +// map(0x01800, 0x01bff) // Pulse Width Modulation +// map(0x02000, 0x023ff) // PIO (Port) +// map(0x02004, 0x02007).rw(FUNC(vrender0soc_device::PIO_r), FUNC(vrender0soc_device::PIO_w)); // PIOLDAT +// map(0x02008, 0x0200b) // PIOEDAT +// map(0x02400, 0x027ff) // Peripheral Chip Select +// map(0x02800, 0x02bff) // SIO +// map(0x03400, 0x037ff) // CRT Controller + map(0x03400, 0x037ff).rw(FUNC(vrender0soc_device::crtc_r), FUNC(vrender0soc_device::crtc_w)).share("crtcregs"); +// map(0x04000, 0x043ff) // RAMDAC & PLL +} + + +//------------------------------------------------- +// device_add_mconfig - device-specific machine +// configuration addiitons +//------------------------------------------------- + +void vrender0soc_device::device_add_mconfig(machine_config &config) +{ + // ... + +} + + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void vrender0soc_device::device_start() +{ + m_host_space = &m_host_cpu->space(AS_PROGRAM); + m_idleskip_cb.resolve_safe(); + + for (int i = 0; i < 4; i++) + m_Timer[i] = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(vrender0soc_device::Timercb),this), (void*)(uintptr_t)i); + + save_item(NAME(m_inten)); + save_item(NAME(m_intst)); + save_item(NAME(m_IntHigh)); + + save_pointer(NAME(m_timer_control), 4); + save_pointer(NAME(m_timer_count), 4); + save_item(NAME(m_dma[0].src)); + save_item(NAME(m_dma[0].dst)); + save_item(NAME(m_dma[0].size)); + save_item(NAME(m_dma[0].ctrl)); + + save_item(NAME(m_dma[1].ctrl)); + save_item(NAME(m_dma[1].src)); + save_item(NAME(m_dma[1].dst)); + save_item(NAME(m_dma[1].size)); +} + + +//------------------------------------------------- +// device_reset - device-specific reset +//------------------------------------------------- + +void vrender0soc_device::device_reset() +{ + // TODO: improve CRT defaults + m_crtcregs[1] = 0x00000022; + + //m_FlipCount = 0; + m_IntHigh = 0; + + m_dma[0].ctrl = 0; + m_dma[1].ctrl = 0; + + for (int i = 0; i < 4; i++) + { + m_timer_control[i] = 0xff << 8; + m_Timer[i]->adjust(attotime::never); + } +} + + +//************************************************************************** +// READ/WRITE HANDLERS +//************************************************************************** + +/* + * + * INT Controller + * + */ + +READ32_MEMBER(vrender0soc_device::intvec_r) +{ + return (m_IntHigh & 7) << 8; +} + +WRITE32_MEMBER(vrender0soc_device::intvec_w) +{ + if (ACCESSING_BITS_0_7) + { + m_intst &= ~(1 << (data & 0x1f)); + if (!m_intst) + m_host_cpu->set_input_line(SE3208_INT, CLEAR_LINE); + } + if (ACCESSING_BITS_8_15) + m_IntHigh = (data >> 8) & 7; +} + +READ32_MEMBER( vrender0soc_device::inten_r ) +{ + return m_inten; +} + +WRITE32_MEMBER( vrender0soc_device::inten_w ) +{ + COMBINE_DATA(&m_inten); + // P'S Attack has a timer 0 irq service with no call to intvec_w but just this + m_intst &= m_inten; + if (!m_intst) + m_host_cpu->set_input_line(SE3208_INT, CLEAR_LINE); +} + +READ32_MEMBER( vrender0soc_device::intst_r ) +{ + return m_intst; +} + +WRITE32_MEMBER( vrender0soc_device::intst_w ) +{ + // TODO: contradicts with documentation, games writes to this? + // ... +} + +void vrender0soc_device::IntReq( int num ) +{ + if (m_inten & (1 << num)) + { + m_intst |= (1 << num); + m_host_cpu->set_input_line(SE3208_INT, ASSERT_LINE); + } + + m_idleskip_cb(ASSERT_LINE); +} + + +int vrender0soc_device::irq_callback() +{ + for (int i = 0; i < 32; ++i) + { + if (BIT(m_intst, i)) + { + return (m_IntHigh << 5) | i; + } + } + return 0; //This should never happen +} + + +/* + * + * Timer + * + */ + + +void vrender0soc_device::TimerStart(int which) +{ + int PD = (m_timer_control[which] >> 8) & 0xff; + int TCV = m_timer_count[which] & 0xffff; + attotime period = attotime::from_hz(14318180 * 3) * ((PD + 1) * (TCV + 1)); // TODO : related to CPU clock + m_Timer[which]->adjust(period); + +// printf("timer %d start, PD = %x TCV = %x period = %s\n", which, PD, TCV, period.as_string()); +} + +TIMER_CALLBACK_MEMBER(vrender0soc_device::Timercb) +{ + int which = (int)(uintptr_t)ptr; + static const int num[] = { 0, 1, 9, 10 }; + + if (m_timer_control[which] & 2) + TimerStart(which); + else + m_timer_control[which] &= ~1; + + IntReq(num[which]); +} + +template +READ32_MEMBER(vrender0soc_device::tmcon_r) +{ + return m_timer_control[Which]; +} + +template +WRITE32_MEMBER(vrender0soc_device::tmcon_w) +{ + uint32_t old = m_timer_control[Which]; + data = COMBINE_DATA(&m_timer_control[Which]); + + if ((data ^ old) & 1) + { + if (data & 1) + { + TimerStart(Which); + } + else + { + // Timer stop + m_Timer[Which]->adjust(attotime::never); +// printf("timer %d stop\n", Which); + } + } +} + +template +READ16_MEMBER(vrender0soc_device::tmcnt_r) +{ + return m_timer_count[Which] & 0xffff; +} + +template +WRITE16_MEMBER(vrender0soc_device::tmcnt_w) +{ + COMBINE_DATA(&m_timer_count[Which]); +} + +/* + * + * DMA Controller + * + */ + +// helper +// bit 5 and bit 3 of the DMA control don't increment source/destination addresses if enabled. +// At the time of writing P's Attack is the only SW that uses this feature, +// in a work RAM to area $4500000 transfer, probably to extend something ... +inline int vrender0soc_device::dma_setup_hold(uint8_t setting, uint8_t bitmask) +{ + return setting & bitmask ? 0 : (setting & 2) ? 4 : (1 << (setting & 1)); +} + +template READ32_MEMBER(vrender0soc_device::dmasa_r) { return m_dma[Which].src; } +template WRITE32_MEMBER(vrender0soc_device::dmasa_w) { COMBINE_DATA(&m_dma[Which].src); } +template READ32_MEMBER(vrender0soc_device::dmada_r) { return m_dma[Which].dst; } +template WRITE32_MEMBER(vrender0soc_device::dmada_w) { COMBINE_DATA(&m_dma[Which].dst); } +template READ32_MEMBER(vrender0soc_device::dmatc_r) { return m_dma[Which].size; } +template WRITE32_MEMBER(vrender0soc_device::dmatc_w) { COMBINE_DATA(&m_dma[Which].size); } +template READ32_MEMBER(vrender0soc_device::dmac_r) { return m_dma[Which].ctrl; } +template +WRITE32_MEMBER(vrender0soc_device::dmac_w) +{ + if (((data ^ m_dma[Which].ctrl) & (1 << 10)) && (data & (1 << 10))) //DMAOn + { + uint32_t const CTR = data; + uint32_t const SRC = m_dma[Which].src; + uint32_t const DST = m_dma[Which].dst; + uint32_t const CNT = m_dma[Which].size; + const int src_inc = dma_setup_hold(CTR, 0x20); + const int dst_inc = dma_setup_hold(CTR, 0x08); + + if ((CTR & 0xd4) != 0) + popmessage("DMA%d with unhandled mode %02x, contact MAMEdev",Which,CTR); + + if (CTR & 0x2) //32 bits + { + for (int i = 0; i < CNT; ++i) + { + uint32_t v = m_host_space->read_dword(SRC + i * src_inc); + m_host_space->write_dword(DST + i * dst_inc, v); + } + } + else if (CTR & 0x1) //16 bits + { + for (int i = 0; i < CNT; ++i) + { + uint16_t v = m_host_space->read_word(SRC + i * src_inc); + m_host_space->write_word(DST + i * dst_inc, v); + } + } + else //8 bits + { + for (int i = 0; i < CNT; ++i) + { + uint8_t v = m_host_space->read_byte(SRC + i * src_inc); + m_host_space->write_byte(DST + i * dst_inc, v); + } + } + data &= ~(1 << 10); + // TODO: insta-DMA + m_dma[Which].size = 0; + IntReq(7 + Which); + } + COMBINE_DATA(&m_dma[Which].ctrl); +} + +READ32_MEMBER(vrender0soc_device::crtc_r) +{ + uint32_t res = m_crtcregs[offset]; + uint32_t hdisp = (m_crtcregs[0x0c / 4] + 1); + uint32_t vdisp = (m_crtcregs[0x1c / 4] + 1); + switch (offset) + { + case 0: // CRTC Status / Mode + if (crt_is_interlaced()) // Interlace + vdisp <<= 1; + + if (m_host_screen->vpos() <= vdisp) // Vertical display enable status + res |= 0x4000; + + if (m_host_screen->hpos() > hdisp) // horizontal & vertical blank period + res &= ~0x2000; + else + res |= 0x2000; + + break; + default: + break; + } + return res; +} + +WRITE32_MEMBER(vrender0soc_device::crtc_w) +{ + if (((m_crtcregs[0] & 0x0100) == 0x0100) && (offset > 0)) // Write protect + return; + + uint32_t old = m_crtcregs[offset]; + switch (offset * 4) + { + case 0: // CRTC Status / Mode Register (CRTMOD) + mem_mask &= ~0xfffffc00; // Bit 31-10 Reserved + break; + case 0x04: // CRTC Timing Control Register (CRTTIM) + mem_mask &= ~0xffffc000; // Bit 31-14 Reserved + break; + case 0x08: // Horizontal Sync Width / Back Porch Register (HSWBP) + mem_mask &= ~0xffff0000; // Bit 31-16 Reserved + break; + case 0x0c: // Horizontal Display Total Register (HDISP) + mem_mask &= ~0xfffffc00; // Bit 31-10 Reserved + break; + case 0x10: // Horizontal Sync Front Porch Register (HSFP) + mem_mask &= ~0xfffffe00; // Bit 31-9 Reserved + break; + case 0x14: // Field Window Bound Register (FWINB) + mem_mask &= ~0xffff80c0; // Bit 31-15, 7-6 Reserved + break; + case 0x18: // Vertical Sync Back Porch Register (VSBP) + mem_mask &= ~0xffffff00; // Bit 31-8 Reserved + break; + case 0x1c: // Vertical Display Total Register (VDISP) + mem_mask &= ~0xfffffe00; // Bit 31-9 Reserved + break; + case 0x20: // Horizontal Total Register (HTOT) + mem_mask &= ~0xffffe000; // Bit 31-13 Reserved + if (BIT(data, 10) == 0) // enable bit + return; + break; + case 0x24: // Vertical Total Register (VTOT) + mem_mask &= ~0xfffff000; // Bit 31-12 Reserved + if (BIT(data, 11) == 0) // enable bit + return; + break; + case 0x28: // Horizontal Line Back Porch Register (HLBP) + mem_mask &= ~0xfffffc00; // Bit 31-10 Reserved + break; + case 0x2c: // CRT Display Start Address 0 Register (STAD0) + mem_mask &= ~0xffff8000; // Bit 31-15 Reserved + break; + case 0x30: // CRT Display Start Address 1 Register (STAD1) + mem_mask &= ~0xffff8000; // Bit 31-15 Reserved + break; + case 0x38: // Light Pen 0 X Register (LIGHT0X) + mem_mask &= ~0xfffff800; // Bit 31-11 Reserved + break; + case 0x3c: // Light Pen 0 Y Register (LIGHT0Y) + mem_mask &= ~0xfffffe00; // Bit 31-9 Reserved + break; + case 0x40: // Light Pen 1 X Register (LIGHT1X) + mem_mask &= ~0xfffff800; // Bit 31-11 Reserved + break; + case 0x44: // Light Pen 1 Y Register (LIGHT1Y) + mem_mask &= ~0xfffffe00; // Bit 31-9 Reserved + break; + case 0x48: // Light Pen Input Control Register (LIGHTC) + mem_mask &= ~0xfffffffc; // Bit 31-2 Reserved + break; + default: + return; + } + COMBINE_DATA(&m_crtcregs[offset]); + if (old ^ m_crtcregs[offset]) + crtc_update(); + +} + +inline bool vrender0soc_device::crt_is_interlaced() +{ + return (m_crtcregs[0x30 / 4] & 1) == 0; +} + +bool vrender0soc_device::crt_active_vblank_irq() +{ + if (crt_is_interlaced() == false) + return true; + + // bit 3 of CRTC reg -> select display start + return (m_host_screen->frame_number() & 1) ^ ((m_crtcregs[0] & 8) >> 3); +} + +void vrender0soc_device::crtc_update() +{ + uint32_t hdisp = m_crtcregs[0x0c / 4] + 1; + uint32_t vdisp = m_crtcregs[0x1c / 4]; + if (hdisp == 0 || vdisp == 0) + return; + + bool interlace_mode = crt_is_interlaced(); + + if (interlace_mode) + vdisp <<= 1; + + uint32_t htot = (m_crtcregs[0x20 / 4] & 0x3ff) + 1; + uint32_t vtot = (m_crtcregs[0x24 / 4] & 0x7ff); + + // adjust htotal in case it's not setup by the game + // (datasheet mentions that it can be done automatically shrug): + // - the two Sealy games do that + // - Cross Puzzle sets up an HTotal of 400 with 640x480 display + // - donghaer writes a 0 to the htot when entering interlace mode + // TODO: we may as well just ditch reading from HTOTAL and VTOTAL and use these instead + if (htot <= 1 || htot <= hdisp) + { + uint32_t hbp = (m_crtcregs[0x08 / 4] & 0xff00) >> 8; + uint32_t hsw = (m_crtcregs[0x08 / 4] & 0xff); + uint32_t hsfp = m_crtcregs[0x10 / 4] & 0xff; + if (hbp == 0 && hsw == 0 && hsfp == 0) + return; + + htot = hdisp + (hbp+1) + (hsw+1) + (hsfp+1); + m_crtcregs[0x20 / 4] = ((htot & 0x3ff) - 1); + } + + // urachamu + if (vtot == 0) + { + uint32_t vbp = (m_crtcregs[0x08 / 4] & 0xff); + if (vbp == 0) + return; + + vtot = vdisp + (vbp + 1); + m_crtcregs[0x24 / 4] = ((vtot & 0x7ff) - 1); + } + + // TODO: the two Sealy games doesn't set this, eventually need to parametrize this one up + uint32_t pixel_clock = (BIT(m_crtcregs[0x04 / 4], 3)) ? 14318180 : 14318180*2; + if (BIT(m_crtcregs[0x04 / 4], 7)) + pixel_clock *= 2; + // TODO: divider setting = 0 is reserved, guess it just desyncs the signal? + pixel_clock /= (m_crtcregs[0x04 / 4] & 7) + 1; + + //printf("DCLK divider %d\n",(m_crtcregs[0x04 / 4] & 7) + 1); + //printf("VCLK select %d\n",(m_crtcregs[0x04 / 4] & 8)); + //printf("CBCLK divider %d\n",((m_crtcregs[0x04 / 4] & 0x70) >> 4) + 1); + //printf("ivclk speed %d\n",(m_crtcregs[0x04 / 4] & 0x80)); + + if (interlace_mode == false) + { + vtot >>= 1; + vtot += 1; + } + //else + // pixel_clock >>= 1; + + + vtot += 9; + + //printf("%dX%d %dX%d %d\n",htot, vtot, hdisp, vdisp, pixel_clock); + + rectangle const visarea(0, hdisp - 1, 0, vdisp - 1); + m_host_screen->configure(htot, vtot, visarea, HZ_TO_ATTOSECONDS(pixel_clock) * vtot * htot); +} + +// accessed by cross puzzle +READ32_MEMBER(vrender0soc_device::sysid_r) +{ + // Device ID: VRender0+ -> 0x0a + // Revision Number -> 0x00 + return 0x00000a00; +} + +READ32_MEMBER(vrender0soc_device::cfgr_r) +{ + // TODO: this truly needs real HW verification + // -x-- ---- Main Clock select (0 -> External Clock) + // --xx x--- Reserved for Chip Test Mode + // ---- -xx- Local ROM Data Bus Width (01 -> 16 bit) + // ---- ---x Local Memory Bus Width (0 -> 16 bit) + return 0x00000002; +} -- cgit v1.2.3