summaryrefslogblamecommitdiffstatshomepage
path: root/src/mame/machine/315_5296.cpp
blob: 310a0bbd820919c072064ef9a606a8f74bbce265 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
                       
                                           


                                                                       
 
                                                                        
                                                                           

                                                          











                                                                            

                                                                       
                             







                                                                        
                                                                                                                           
                                                                                                         




































                                                                     
 







                                                                    
 


                                                                       
 













                                                   
 





                                                          

                                        






                                                   
                       
 






                                                                                                
 

                                                               
 
















                                                   
 

                              
         
 





                                            
                       










                                                                                               
 

                               


                                                                                                    

                                                                  
 



                                                                                             










                                                                                                                
 

                                     
 



                              
// license:BSD-3-Clause
// copyright-holders:hap, Charles MacDonald
/**********************************************************************

    Sega 315-5296 I/O chip

    Sega 100-pin QFP, with 8 bidirectional I/O ports, and 3 output pins.
    It also has chip select(/FMCS) and clock(CKOT) for a peripheral device.
    Commonly used from the late 80s up until Sega Model 2.

    The I/O chip has 64 addresses:
    $00-0F : I/O ports, security, configuration registers
    $10-1F : Unused (no effect when read or written)
    $20-3F : Unused (enables /FMCS output, eg. to YM2151 /CS)

    On System 16 derivatives, the unused locations return the 68000 prefetch
    value off the bus when read.


    TODO:
    - complete emulation of CNT register

**********************************************************************/

#include "machine/315_5296.h"


const device_type SEGA_315_5296 = &device_creator<sega_315_5296_device>;

//-------------------------------------------------
//  sega_315_5296_device - constructor
//-------------------------------------------------

sega_315_5296_device::sega_315_5296_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: device_t(mconfig, SEGA_315_5296, "Sega 315-5296 I/O", tag, owner, clock, "315_5296", __FILE__),
	m_in_pa_cb(*this),
	m_in_pb_cb(*this),
	m_in_pc_cb(*this),
	m_in_pd_cb(*this),
	m_in_pe_cb(*this),
	m_in_pf_cb(*this),
	m_in_pg_cb(*this),
	m_in_ph_cb(*this),
	m_out_pa_cb(*this),
	m_out_pb_cb(*this),
	m_out_pc_cb(*this),
	m_out_pd_cb(*this),
	m_out_pe_cb(*this),
	m_out_pf_cb(*this),
	m_out_pg_cb(*this),
	m_out_ph_cb(*this),
	m_out_cnt0_cb(*this),
	m_out_cnt1_cb(*this),
	m_out_cnt2_cb(*this)
{
}

//-------------------------------------------------
//  device_start - device-specific startup
//-------------------------------------------------

void sega_315_5296_device::device_start()
{
	// resolve callbacks
	m_in_pa_cb.resolve_safe(0xff); m_in_port_cb[0] = &m_in_pa_cb;
	m_in_pb_cb.resolve_safe(0xff); m_in_port_cb[1] = &m_in_pb_cb;
	m_in_pc_cb.resolve_safe(0xff); m_in_port_cb[2] = &m_in_pc_cb;
	m_in_pd_cb.resolve_safe(0xff); m_in_port_cb[3] = &m_in_pd_cb;
	m_in_pe_cb.resolve_safe(0xff); m_in_port_cb[4] = &m_in_pe_cb;
	m_in_pf_cb.resolve_safe(0xff); m_in_port_cb[5] = &m_in_pf_cb;
	m_in_pg_cb.resolve_safe(0xff); m_in_port_cb[6] = &m_in_pg_cb;
	m_in_ph_cb.resolve_safe(0xff); m_in_port_cb[7] = &m_in_ph_cb;

	m_out_pa_cb.resolve_safe(); m_out_port_cb[0] = &m_out_pa_cb;
	m_out_pb_cb.resolve_safe(); m_out_port_cb[1] = &m_out_pb_cb;
	m_out_pc_cb.resolve_safe(); m_out_port_cb[2] = &m_out_pc_cb;
	m_out_pd_cb.resolve_safe(); m_out_port_cb[3] = &m_out_pd_cb;
	m_out_pe_cb.resolve_safe(); m_out_port_cb[4] = &m_out_pe_cb;
	m_out_pf_cb.resolve_safe(); m_out_port_cb[5] = &m_out_pf_cb;
	m_out_pg_cb.resolve_safe(); m_out_port_cb[6] = &m_out_pg_cb;
	m_out_ph_cb.resolve_safe(); m_out_port_cb[7] = &m_out_ph_cb;

	m_out_cnt0_cb.resolve_safe(); m_out_cnt_cb[0] = &m_out_cnt0_cb;
	m_out_cnt1_cb.resolve_safe(); m_out_cnt_cb[1] = &m_out_cnt1_cb;
	m_out_cnt2_cb.resolve_safe(); m_out_cnt_cb[2] = &m_out_cnt2_cb;

	// register for savestates
	save_item(NAME(m_output_latch));
	save_item(NAME(m_cnt));
	save_item(NAME(m_dir));
}

//-------------------------------------------------
//  device_reset - device-specific reset
//-------------------------------------------------

void sega_315_5296_device::device_reset()
{
	// all ports are set to input
	m_dir = 0;

	// clear output ports
	memset(m_output_latch, 0, sizeof(m_output_latch));
	m_cnt = 0;

	for (int i = 0; i < 8; i++)
		(*m_out_port_cb[i])((offs_t)i, 0);
	for (auto & elem : m_out_cnt_cb)
		(*elem)(0);
}


//-------------------------------------------------

READ8_MEMBER( sega_315_5296_device::read )
{
	offset &= 0x3f;

	switch (offset)
	{
		// port A to H
		case 0x0: case 0x1: case 0x2: case 0x3: case 0x4: case 0x5: case 0x6: case 0x7:
			// if the port is configured as an output, return the last thing written
			if (m_dir & 1 << offset)
				return m_output_latch[offset];

			// otherwise, return an input port
			return (*m_in_port_cb[offset])(offset);

		// 'SEGA' protection
		case 0x8:
			return 'S';
		case 0x9:
			return 'E';
		case 0xa:
			return 'G';
		case 0xb:
			return 'A';

		// CNT register & mirror
		case 0xc: case 0xe:
			return m_cnt;

		// port direction register & mirror
		case 0xd: case 0xf:
			return m_dir;

		default:
			break;
	}

	return 0xff;
}


WRITE8_MEMBER( sega_315_5296_device::write )
{
	offset &= 0x3f;

	switch (offset)
	{
		// port A to H
		case 0x0: case 0x1: case 0x2: case 0x3: case 0x4: case 0x5: case 0x6: case 0x7:
			// if the port is configured as an output, write it
			if (m_dir & 1 << offset)
				(*m_out_port_cb[offset])(offset, data);

			m_output_latch[offset] = data;
			break;

		// CNT register
		case 0xe:
			// d0-2: CNT0-2 output pins
			// note: When CNT2 is configured as clock output, bit 2 of this register has
			// no effect on the output level of CNT2.
			for (int i = 0; i < 3; i++)
				(*m_out_cnt_cb[i])(data >> i & 1);

			// d3: CNT2 output mode (1= Clock output, 0= Programmable output)
			// d4,5: CNT2 clock divider (0= CLK/4, 1= CLK/8, 2= CLK/16, 3= CLK/2)
			// d6,7: CKOT clock divider (0= CLK/4, 1= CLK/8, 2= CLK/16, 3= CLK/2)
			// TODO..
			m_cnt = data;
			break;

		// port direction register
		case 0xf:
			// refresh output ports if the direction changed
			for (int i = 0; i < 8; i++)
			{
				if ((m_dir ^ data) & (1 << i))
					(*m_out_port_cb[i])((offs_t)i, (data & 1 << i) ? m_output_latch[i] : 0);
			}

			m_dir = data;
			break;

		default:
			break;
	}
}