summaryrefslogtreecommitdiffstatshomepage
path: root/src/mame/machine/jaleco_ms32_sysctrl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mame/machine/jaleco_ms32_sysctrl.cpp')
-rw-r--r--src/mame/machine/jaleco_ms32_sysctrl.cpp368
1 files changed, 368 insertions, 0 deletions
diff --git a/src/mame/machine/jaleco_ms32_sysctrl.cpp b/src/mame/machine/jaleco_ms32_sysctrl.cpp
new file mode 100644
index 00000000000..55fffddd8bb
--- /dev/null
+++ b/src/mame/machine/jaleco_ms32_sysctrl.cpp
@@ -0,0 +1,368 @@
+// license:BSD-3-Clause
+// copyright-holders:Angelo Salese, Alex Marshall
+/******************************************************************************
+
+ Jaleco MS32 System Control Unit
+
+ A simple system controller for late 90s Jaleco HWs
+
+ Known features:
+ - CRTC & screen(s?) control;
+ - dot clock control;
+ - irq/reset controller;
+ - programmable timer;
+ - watchdog;
+
+ First use in MS32, then their later (?) 68k revision.
+
+ TODO:
+ - pinpoint exact timing generation for programmable irq
+ (free counter or based on host screen beams)
+ - interface with multiple screens is a mystery,
+ cfr. dual screen bnstars, stepping stage HW.
+ Most likely former effectively controls both screens in demux while
+ latter has no way to set the other screen(s)?
+ - watchdog timing;
+ - upper address line seems unconnected by 68k,
+ and is it a mystery how watchdog is supposed to route here and assuming
+ it is and not actually disabled by pin.
+ - network/COPROs irq connections, specifically for f1superb;
+ - actual chip name;
+
+ BTANBs:
+ - in p47aces v1.0 (p47acesa) code messes up the prg irq timer setup,
+ causing SFX overloads by using Spitfire ship with 30 Hz autofire
+ and shooting at point blank range over walls/enemies.
+ This has been fixed in v1.1
+
+*******************************************************************************/
+
+#include "emu.h"
+#include "jaleco_ms32_sysctrl.h"
+
+//*****************************************************************************
+// GLOBAL VARIABLES
+//*****************************************************************************
+
+// device type definition
+DEFINE_DEVICE_TYPE(JALECO_MS32_SYSCTRL, jaleco_ms32_sysctrl_device, "jaleco_ms32_sysctrl", "Jaleco MS32 System Control Unit")
+
+
+//*****************************************************************************
+// LIVE DEVICE
+//*****************************************************************************
+
+//-------------------------------------------------
+// jaleco_ms32_sysctrl_device - constructor
+//-------------------------------------------------
+
+jaleco_ms32_sysctrl_device::jaleco_ms32_sysctrl_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : device_t(mconfig, JALECO_MS32_SYSCTRL, tag, owner, clock)
+ , m_screen(*this, finder_base::DUMMY_TAG)
+ , m_flip_screen_cb(*this)
+ , m_vblank_cb(*this)
+ , m_field_cb(*this)
+ , m_prg_timer_cb(*this)
+ , m_sound_ack_cb(*this)
+ , m_sound_reset_cb(*this)
+ , m_invert_vblank_lines(false)
+{
+}
+
+void jaleco_ms32_sysctrl_device::amap(address_map& map)
+{
+// 0xba0000 in 68k, 0xfce00000 in MS32 mapped at lower 16-bits mask
+ map(0x00, 0x01).w(FUNC(jaleco_ms32_sysctrl_device::control_w));
+ map(0x02, 0x03).w(FUNC(jaleco_ms32_sysctrl_device::hblank_w));
+ map(0x04, 0x05).w(FUNC(jaleco_ms32_sysctrl_device::hdisplay_w));
+ map(0x06, 0x07).w(FUNC(jaleco_ms32_sysctrl_device::hbp_w));
+ map(0x08, 0x09).w(FUNC(jaleco_ms32_sysctrl_device::hfp_w));
+ map(0x0a, 0x0b).w(FUNC(jaleco_ms32_sysctrl_device::vblank_w));
+ map(0x0c, 0x0d).w(FUNC(jaleco_ms32_sysctrl_device::vdisplay_w));
+ map(0x0e, 0x0f).w(FUNC(jaleco_ms32_sysctrl_device::vbp_w));
+ map(0x10, 0x11).w(FUNC(jaleco_ms32_sysctrl_device::vfp_w));
+ map(0x18, 0x19).w(FUNC(jaleco_ms32_sysctrl_device::timer_interval_w));
+ map(0x1a, 0x1b).w(FUNC(jaleco_ms32_sysctrl_device::timer_ack_w));
+ map(0x1c, 0x1d).w(FUNC(jaleco_ms32_sysctrl_device::sound_reset_w));
+ map(0x1e, 0x1f).w(FUNC(jaleco_ms32_sysctrl_device::irq_ack_w));
+// map(0x24, 0x27).w // sound comms bidirectional acks?
+ map(0x26, 0x27).w(FUNC(jaleco_ms32_sysctrl_device::sound_ack_w));
+ map(0x28, 0x29).nopw(); // watchdog on MS32
+ map(0x2c, 0x2d).w(FUNC(jaleco_ms32_sysctrl_device::field_ack_w));
+ map(0x2e, 0x2f).w(FUNC(jaleco_ms32_sysctrl_device::vblank_ack_w));
+}
+
+//-------------------------------------------------
+// device_add_mconfig - device-specific machine
+// configuration addiitons
+//-------------------------------------------------
+
+void jaleco_ms32_sysctrl_device::device_add_mconfig(machine_config &config)
+{
+// TIMER(config, "scantimer").configure_scanline(FUNC(jaleco_ms32_sysctrl_device::scanline_cb), m_screen, 0, 1);
+
+ // TODO: watchdog
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void jaleco_ms32_sysctrl_device::device_resolve_objects()
+{
+ m_flip_screen_cb.resolve();
+ m_vblank_cb.resolve();
+ m_field_cb.resolve();
+ m_prg_timer_cb.resolve();
+ m_sound_reset_cb.resolve();
+ m_sound_ack_cb.resolve();
+}
+
+void jaleco_ms32_sysctrl_device::device_start()
+{
+ save_item(NAME(m_dotclock_setting));
+ save_item(NAME(m_crtc.horz_blank));
+ save_item(NAME(m_crtc.horz_display));
+ save_item(NAME(m_crtc.vert_blank));
+ save_item(NAME(m_crtc.vert_display));
+ save_item(NAME(m_flip_screen_state));
+ save_item(NAME(m_timer.irq_enable));
+
+ m_timer.prg_irq = timer_alloc(PRG_TIMER);
+ m_timer_scanline = timer_alloc(SCANLINE_TIMER);
+}
+
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void jaleco_ms32_sysctrl_device::device_reset()
+{
+ m_dotclock_setting = 0;
+ m_crtc.horz_blank = 64;
+ m_crtc.horz_display = 320;
+ m_crtc.vert_blank = 39;
+ m_crtc.vert_display = 224;
+ m_timer.irq_enable = false;
+ m_timer.interval = 1;
+ flush_prg_timer();
+// flush_scanline_timer(m_crtc.vert_display);
+ m_timer_scanline->adjust(attotime::never);
+ // put flipping in a default state
+ m_flip_screen_state = false;
+ m_flip_screen_cb(0);
+}
+
+void jaleco_ms32_sysctrl_device::flush_prg_timer()
+{
+ attotime step;
+ if (m_timer.irq_enable)
+ {
+ //const u16 htotal = m_crtc.horz_blank + m_crtc.horz_display;
+ // TODO: unknown actual timings, with interval = 2 it should fire an irq every 16 scanlines in p47aces v1.1
+ // (excluding times where it is disabled) -> ~1000 Hz?
+ step = attotime::from_nsec(500000) * m_timer.interval;
+ }
+ else
+ {
+ step = attotime::never;
+ m_prg_timer_cb(0);
+ }
+ m_timer.prg_irq->adjust(step);
+}
+
+void jaleco_ms32_sysctrl_device::flush_scanline_timer(int current_scanline)
+{
+ // in typical Jaleco fashion (cfr. mega system 1), both irqs are somehow configurable (a pin?).
+ // Examples are tp2ms32 and wpksocv2, wanting vblank as vector 9 and field as 10 otherwise they runs
+ // at half speed, but then their config can't possibly work with p47aces (i.e. wants 10 and 9 respectively),
+ // plus bnstars that locks up off the bat if the wrong irq runs at 60 Hz.
+ // We currently hardwire via an init time setter here, making the irq acks to trigger properly as well.
+
+ if (current_scanline == m_crtc.vert_display)
+ m_invert_vblank_lines ? m_field_cb(1) : m_vblank_cb(1);
+
+ // 30 Hz irq
+ // TODO: unknown mechanics where this happens, is it even tied to scanline?
+ if (current_scanline == 0 && m_screen->frame_number() & 1)
+ m_invert_vblank_lines ? m_vblank_cb(1) : m_field_cb(1);
+
+ uint32_t next_scanline = (current_scanline + 1) % crtc_vtotal();
+ m_timer_scanline->adjust(m_screen->time_until_pos(next_scanline), next_scanline);
+}
+
+void jaleco_ms32_sysctrl_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ switch (id)
+ {
+ case SCANLINE_TIMER:
+ flush_scanline_timer(param);
+ break;
+ case PRG_TIMER:
+ m_prg_timer_cb(1);
+ flush_prg_timer();
+ break;
+ }
+}
+
+//*****************************************************************************
+// READ/WRITE HANDLERS
+//*****************************************************************************
+
+static constexpr u16 clamp_to_12bits_neg(u16 raw_data)
+{
+ // each write has a 12 bit resolution, for both CRTC and timer interval
+ // TODO: nndmseal: on POST it sets up a 0x1000 for vblank, possibly for screen disable?
+ // TODO: rockn2: on POST it sets up a vertical size of 487, is it trying to setup an interlace setting?
+ return 0x1000 - (raw_data & 0xfff);
+}
+
+// ============================================================================
+// CRTC
+// ============================================================================
+
+inline u32 jaleco_ms32_sysctrl_device::get_dotclock_frequency()
+{
+ const u8 dot_divider = m_dotclock_setting & 1 ? 6 : 8;
+ return clock() / dot_divider;
+}
+
+inline u16 jaleco_ms32_sysctrl_device::crtc_vtotal()
+{
+ return m_crtc.vert_blank + m_crtc.vert_display;
+}
+
+inline void jaleco_ms32_sysctrl_device::crtc_refresh_screen_params()
+{
+ rectangle visarea;
+ const u16 htotal = m_crtc.horz_blank + m_crtc.horz_display;
+ const u16 vtotal = crtc_vtotal();
+ const attoseconds_t refresh = HZ_TO_ATTOSECONDS(get_dotclock_frequency()) * htotal * vtotal;
+ visarea.set(0, m_crtc.horz_display - 1, 0, m_crtc.vert_display - 1);
+ logerror("%s: CRTC setup total: %d x %d display: %d x %d\n", this->tag(), htotal, vtotal, m_crtc.horz_display, m_crtc.vert_display);
+ m_screen->configure(htotal, vtotal, visarea, refresh);
+ m_timer_scanline->adjust(m_screen->time_until_pos(vtotal), vtotal);
+}
+
+void jaleco_ms32_sysctrl_device::control_w(u16 data)
+{
+ /*
+ * ---- x--- programmable irq timer enable
+ * ---- -x-- used by f1superb, stepstag, bnstars
+ * ---- --x- flip screen
+ * ---- ---x dotclock select (1) 8 MHz (0) 6 MHz
+ */
+ if (BIT(data, 0) != m_dotclock_setting)
+ {
+ m_dotclock_setting = BIT(data, 0);
+ crtc_refresh_screen_params();
+ }
+
+ const bool current_flip = bool(BIT(data, 1));
+ if (current_flip != m_flip_screen_state)
+ {
+ m_flip_screen_state = current_flip;
+ m_flip_screen_cb(m_flip_screen_state ? ASSERT_LINE : CLEAR_LINE);
+ }
+ if (data & 0xf4)
+ logerror("%s: enabled unknown bit in control_w %02x\n", this->tag(), data & 0xf4);
+
+ m_timer.irq_enable = bool(BIT(data, 3));
+ flush_prg_timer();
+}
+
+void jaleco_ms32_sysctrl_device::hblank_w(u16 data)
+{
+ m_crtc.horz_blank = clamp_to_12bits_neg(data);
+ crtc_refresh_screen_params();
+}
+
+void jaleco_ms32_sysctrl_device::hdisplay_w(u16 data)
+{
+ m_crtc.horz_display = clamp_to_12bits_neg(data);
+ crtc_refresh_screen_params();
+}
+
+void jaleco_ms32_sysctrl_device::hbp_w(u16 data)
+{
+ logerror("%s: HSYNC back porch %d\n", this->tag(), 0x1000 - data);
+}
+
+void jaleco_ms32_sysctrl_device::hfp_w(u16 data)
+{
+ logerror("%s: HSYNC front porch %d\n", this->tag(), 0x1000 - data);
+}
+
+void jaleco_ms32_sysctrl_device::vblank_w(u16 data)
+{
+ m_crtc.vert_blank = clamp_to_12bits_neg(data);
+ crtc_refresh_screen_params();
+}
+
+void jaleco_ms32_sysctrl_device::vdisplay_w(u16 data)
+{
+ m_crtc.vert_display = clamp_to_12bits_neg(data);
+ crtc_refresh_screen_params();
+}
+
+void jaleco_ms32_sysctrl_device::vbp_w(u16 data)
+{
+ logerror("%s: VSYNC back porch %d\n", this->tag(), clamp_to_12bits_neg(data));
+}
+
+void jaleco_ms32_sysctrl_device::vfp_w(u16 data)
+{
+ logerror("%s: VSYNC front porch %d\n", this->tag(), clamp_to_12bits_neg(data));
+}
+
+// ============================================================================
+// Timer
+// ============================================================================
+
+void jaleco_ms32_sysctrl_device::timer_interval_w(u16 data)
+{
+ m_timer.interval = clamp_to_12bits_neg(data);
+ flush_prg_timer();
+}
+
+void jaleco_ms32_sysctrl_device::timer_ack_w(u16 data)
+{
+ m_prg_timer_cb(0);
+}
+
+// ============================================================================
+// ACK lines
+// ============================================================================
+
+void jaleco_ms32_sysctrl_device::sound_reset_w(u16 data)
+{
+ m_sound_reset_cb(data & 1);
+ logerror("%s: sound_reset_w %02x\n", this->tag(), data);
+}
+
+void jaleco_ms32_sysctrl_device::vblank_ack_w(u16 data)
+{
+ m_vblank_cb(0);
+}
+
+void jaleco_ms32_sysctrl_device::field_ack_w(u16 data)
+{
+ m_field_cb(0);
+}
+
+void jaleco_ms32_sysctrl_device::sound_ack_w(u16 data)
+{
+ m_sound_ack_cb(1);
+}
+
+void jaleco_ms32_sysctrl_device::irq_ack_w(u16 data)
+{
+ // guess: 68k games calls this in vblank routine instead of
+ // the designated line, maybe it's a 68k version difference
+ // or maybe this is right
+ m_vblank_cb(0);
+ m_field_cb(0);
+ // TODO: f1superb definitely clears comms irq with this
+}