summaryrefslogblamecommitdiffstatshomepage
path: root/src/mame/machine/jaleco_ms32_sysctrl.cpp
blob: c9007080a663a83a9257b3c1e2ea1c7b9b9fce60 (plain) (tree)
1
2
3
4



                                                                               































                                                                             




































                                                                                                                                       
                                                                    












                                                                              
                                                        







                                                                          
                           



                                                                           
                                                                                                                 



























                                                         
                                          
 


















                                                       



                                                                                    
                                              

























                                                                                                                           

                                                                                                                    









                                                                                                               
 


























































                                                                                                                                            
          









                                                        
 







                                                                                                  
 

















                                                        
                                                                          



                                                
                                                                           

































































                                                                                       

                                                                   




                                                               
// 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 additions
//-------------------------------------------------

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));
	save_item(NAME(m_timer.interval));

	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();
	// bnstars1 never ever set up the CRTC at boot, causing no irq.
	// we currently compensate by basically giving one frame of time,
	// ofc on the real thing the first vblank is really when screen sync occurs.
	flush_scanline_timer(m_crtc.vert_display-1);
//  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
}