diff options
Diffstat (limited to 'src/devices/machine/vrender0.cpp')
-rw-r--r-- | src/devices/machine/vrender0.cpp | 576 |
1 files changed, 576 insertions, 0 deletions
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<int Which> +READ32_MEMBER(vrender0soc_device::tmcon_r) +{ + return m_timer_control[Which]; +} + +template<int Which> +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<int Which> +READ16_MEMBER(vrender0soc_device::tmcnt_r) +{ + return m_timer_count[Which] & 0xffff; +} + +template<int Which> +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<int Which> READ32_MEMBER(vrender0soc_device::dmasa_r) { return m_dma[Which].src; } +template<int Which> WRITE32_MEMBER(vrender0soc_device::dmasa_w) { COMBINE_DATA(&m_dma[Which].src); } +template<int Which> READ32_MEMBER(vrender0soc_device::dmada_r) { return m_dma[Which].dst; } +template<int Which> WRITE32_MEMBER(vrender0soc_device::dmada_w) { COMBINE_DATA(&m_dma[Which].dst); } +template<int Which> READ32_MEMBER(vrender0soc_device::dmatc_r) { return m_dma[Which].size; } +template<int Which> WRITE32_MEMBER(vrender0soc_device::dmatc_w) { COMBINE_DATA(&m_dma[Which].size); } +template<int Which> READ32_MEMBER(vrender0soc_device::dmac_r) { return m_dma[Which].ctrl; } +template<int Which> +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; +} |