diff options
Diffstat (limited to 'src/mame/machine/jaleco_ms32_sysctrl.cpp')
-rw-r--r-- | src/mame/machine/jaleco_ms32_sysctrl.cpp | 368 |
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 +} |