summaryrefslogtreecommitdiffstatshomepage
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/emu/machine/7200fifo.c24
-rw-r--r--src/emu/sound/upd7759.c14
-rw-r--r--src/emu/sound/upd7759.h14
-rw-r--r--src/mame/drivers/segac2.c220
-rw-r--r--src/mame/machine/315-5296.c178
-rw-r--r--src/mame/machine/315-5296.h145
-rw-r--r--src/mame/mame.mak1
7 files changed, 421 insertions, 175 deletions
diff --git a/src/emu/machine/7200fifo.c b/src/emu/machine/7200fifo.c
index 728fc5c8563..f4a6cb758b3 100644
--- a/src/emu/machine/7200fifo.c
+++ b/src/emu/machine/7200fifo.c
@@ -38,9 +38,9 @@ void fifo7200_device::device_start()
m_buffer.resize(m_ram_size);
// resolve callbacks
- m_ef_handler.resolve();
- m_ff_handler.resolve();
- m_hf_handler.resolve();
+ m_ef_handler.resolve_safe();
+ m_ff_handler.resolve_safe();
+ m_hf_handler.resolve_safe();
// state save
save_item(NAME(m_read_ptr));
@@ -65,9 +65,9 @@ void fifo7200_device::device_reset()
m_ff = 0;
m_hf = 0;
- if (!m_ef_handler.isnull()) m_ef_handler(!m_ef);
- if (!m_ff_handler.isnull()) m_ff_handler(!m_ff);
- if (!m_hf_handler.isnull()) m_hf_handler(!m_hf);
+ m_ef_handler(!m_ef);
+ m_ff_handler(!m_ff);
+ m_hf_handler(!m_hf);
}
@@ -87,19 +87,19 @@ void fifo7200_device::fifo_write(UINT16 data)
if (m_ef)
{
m_ef = 0;
- if (!m_ef_handler.isnull()) m_ef_handler(!m_ef);
+ m_ef_handler(!m_ef);
}
else if (m_read_ptr == m_write_ptr)
{
m_ff = 1;
- if (!m_ff_handler.isnull()) m_ff_handler(!m_ff);
+ m_ff_handler(!m_ff);
}
else if (((m_read_ptr + 1 + m_ram_size / 2) % m_ram_size) == m_write_ptr)
{
m_hf = 1;
- if (!m_hf_handler.isnull()) m_hf_handler(!m_hf);
+ m_hf_handler(!m_hf);
}
}
@@ -118,19 +118,19 @@ UINT16 fifo7200_device::fifo_read()
if (m_ff)
{
m_ff = 0;
- if (!m_ff_handler.isnull()) m_ff_handler(!m_ff);
+ m_ff_handler(!m_ff);
}
else if (m_read_ptr == m_write_ptr)
{
m_ef = 1;
- if (!m_ef_handler.isnull()) m_ef_handler(!m_ef);
+ m_ef_handler(!m_ef);
}
else if (((m_read_ptr + m_ram_size / 2) % m_ram_size) == m_write_ptr)
{
m_hf = 0;
- if (!m_hf_handler.isnull()) m_hf_handler(!m_hf);
+ m_hf_handler(!m_hf);
}
return ret;
diff --git a/src/emu/sound/upd7759.c b/src/emu/sound/upd7759.c
index 72157e51f73..942c1aad28e 100644
--- a/src/emu/sound/upd7759.c
+++ b/src/emu/sound/upd7759.c
@@ -736,11 +736,11 @@ void upd775x_device::postload()
*************************************************************/
-void upd775x_device::reset_w(UINT8 data)
+WRITE_LINE_MEMBER( upd775x_device::reset_w )
{
/* update the reset value */
UINT8 oldreset = m_reset;
- m_reset = (data != 0);
+ m_reset = (state != 0);
/* update the stream first */
m_channel->update();
@@ -750,11 +750,11 @@ void upd775x_device::reset_w(UINT8 data)
device_reset();
}
-void upd7759_device::start_w(UINT8 data)
+WRITE_LINE_MEMBER( upd7759_device::start_w )
{
/* update the start value */
UINT8 oldstart = m_start;
- m_start = (data != 0);
+ m_start = (state != 0);
logerror("upd7759_start_w: %d->%d\n", oldstart, m_start);
@@ -772,11 +772,11 @@ void upd7759_device::start_w(UINT8 data)
}
}
-void upd7756_device::start_w(UINT8 data)
+WRITE_LINE_MEMBER( upd7756_device::start_w )
{
/* update the start value */
UINT8 oldstart = m_start;
- m_start = (data != 0);
+ m_start = (state != 0);
logerror("upd7759_start_w: %d->%d\n", oldstart, m_start);
@@ -798,7 +798,7 @@ WRITE8_MEMBER( upd775x_device::port_w )
}
-int upd775x_device::busy_r()
+READ_LINE_MEMBER( upd775x_device::busy_r )
{
/* return /BUSY */
return (m_state == STATE_IDLE);
diff --git a/src/emu/sound/upd7759.h b/src/emu/sound/upd7759.h
index 10d0df4a8f8..948f7224927 100644
--- a/src/emu/sound/upd7759.h
+++ b/src/emu/sound/upd7759.h
@@ -25,8 +25,8 @@ public:
void set_bank_base(offs_t base);
- void reset_w(UINT8 data);
- int busy_r();
+ DECLARE_WRITE_LINE_MEMBER( reset_w );
+ DECLARE_READ_LINE_MEMBER( busy_r );
DECLARE_WRITE8_MEMBER( port_w );
void postload();
@@ -93,9 +93,9 @@ public:
upd7759_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
enum
- {
- TIMER_SLAVE_UPDATE
- };
+ {
+ TIMER_SLAVE_UPDATE
+ };
virtual void device_start();
virtual void device_reset();
@@ -104,7 +104,7 @@ public:
emu_timer *m_timer; /* timer */
- void start_w(UINT8 data);
+ DECLARE_WRITE_LINE_MEMBER( start_w );
};
class upd7756_device : public upd775x_device
@@ -116,7 +116,7 @@ public:
virtual void device_reset();
virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
- void start_w(UINT8 data);
+ DECLARE_WRITE_LINE_MEMBER( start_w );
};
extern const device_type UPD7759;
diff --git a/src/mame/drivers/segac2.c b/src/mame/drivers/segac2.c
index 6cde903f3e1..2f9729935bd 100644
--- a/src/mame/drivers/segac2.c
+++ b/src/mame/drivers/segac2.c
@@ -70,6 +70,7 @@
#include "emu.h"
#include "cpu/m68000/m68000.h"
#include "machine/nvram.h"
+#include "machine/315-5296.h"
#include "sound/okim6295.h"
#include "sound/sn76496.h"
#include "sound/2612intf.h"
@@ -162,11 +163,13 @@ public:
DECLARE_WRITE_LINE_MEMBER(vdp_lv6irqline_callback_c2);
DECLARE_WRITE_LINE_MEMBER(vdp_lv4irqline_callback_c2);
+ DECLARE_READ8_MEMBER(io_portc_r);
+ DECLARE_WRITE8_MEMBER(io_portd_w);
+ DECLARE_WRITE8_MEMBER(io_porth_w);
+
DECLARE_WRITE16_MEMBER( segac2_upd7759_w );
DECLARE_READ16_MEMBER( palette_r );
DECLARE_WRITE16_MEMBER( palette_w );
- DECLARE_READ16_MEMBER( io_chip_r );
- DECLARE_WRITE16_MEMBER( io_chip_w );
DECLARE_WRITE16_MEMBER( control_w );
DECLARE_READ16_MEMBER( prot_r );
DECLARE_WRITE16_MEMBER( prot_w );
@@ -201,7 +204,6 @@ public:
int prot_func_pclubjv2(int in);
int prot_func_pclubjv4(int in);
int prot_func_pclubjv5(int in);
-
};
@@ -255,7 +257,6 @@ MACHINE_RESET_MEMBER(segac2_state,segac2)
m_sp_palbase = 0;
recompute_palette_tables();
-
}
@@ -273,10 +274,10 @@ MACHINE_RESET_MEMBER(segac2_state,segac2)
******************************************************************************/
/* handle writes to the UPD7759 */
-WRITE16_MEMBER(segac2_state::segac2_upd7759_w )
+WRITE16_MEMBER(segac2_state::segac2_upd7759_w)
{
/* make sure we have a UPD chip */
- if (!m_sound_banks)
+ if (m_upd7759 == NULL)
return;
/* only works if we're accessing the low byte */
@@ -307,7 +308,7 @@ WRITE16_MEMBER(segac2_state::segac2_upd7759_w )
******************************************************************************/
/* handle reads from the paletteram */
-READ16_MEMBER(segac2_state::palette_r )
+READ16_MEMBER(segac2_state::palette_r)
{
offset &= 0x1ff;
if (m_segac2_alt_palette_mode)
@@ -317,7 +318,7 @@ READ16_MEMBER(segac2_state::palette_r )
}
/* handle writes to the paletteram */
-WRITE16_MEMBER(segac2_state::palette_w )
+WRITE16_MEMBER(segac2_state::palette_w)
{
int r, g, b, newword;
int tmpr, tmpg, tmpb;
@@ -403,143 +404,63 @@ void segac2_state::recompute_palette_tables()
m_segac2_sp_pal_lookup[i] = 0x200 * m_palbank + ((~sppal << 2) & 0x100) + ((sppal << 2) & 0x80) + ((~sppal >> 2) & 0x40) + ((sppal >> 2) & 0x20) + (sppal & 0x10);
}
}
-
}
/******************************************************************************
- I/O Read & Write Handlers
-*******************************************************************************
-
- Controls, and Poto Poto reads 'S' 'E' 'G' and 'A' (SEGA) from this area
- as a form of protection.
-
- Lots of unknown writes however offset 0E certainly seems to be banking,
- both colours and sound sample banks.
-
+ Sega 315-5296 I/O chip
******************************************************************************/
-READ16_MEMBER(segac2_state::io_chip_r )
-{
- static const char *const portnames[] = { "P1", "P2", "PORTC", "PORTD", "SERVICE", "COINAGE", "DSW", "PORTH" };
- offset &= 0x1f/2;
-
- switch (offset)
+READ8_MEMBER(segac2_state::io_portc_r)
+{
+ // D7 : From MB3773P pin 1. (/RESET output)
+ // D6 : From uPD7759 pin 18. (/BUSY output)
+ int busy = (m_upd7759 != NULL) ? (m_upd7759->busy_r() << 6) : 0x40;
+ return 0xbf | busy;
+
+}
+
+WRITE8_MEMBER(segac2_state::io_portd_w)
+{
+ /*
+ D7 : To pin 3 of JP15. (Watchdog clock control)
+ D6 : To MUTE input pin on TDA1518BQ amplifier.
+ D5 : To CN2 pin 10. (Unknown purpose)
+ D4 : To CN2 pin 11. (Unknown purpose)
+ D3 : To CN1 pin K. (Coin lockout 2)
+ D2 : To CN1 pin 9. (Coin lockout 1)
+ D1 : To CN1 pin J. (Coin meter 2)
+ D0 : To CN1 pin 8. (Coin meter 1)
+ */
+ //coin_lockout_w(space.machine(), 1, data & 0x08);
+ //coin_lockout_w(space.machine(), 0, data & 0x04);
+ coin_counter_w(space.machine(), 1, data & 0x02);
+ coin_counter_w(space.machine(), 0, data & 0x01);
+}
+
+WRITE8_MEMBER(segac2_state::io_porth_w)
+{
+ /*
+ D7 : To pin A19 of CN4
+ D6 : To pin B19 of CN4
+ D5 : ?
+ D4 : ?
+ D3 : To pin 31 of uPD7759 sample ROM (A18 on a 27C040)
+ D2 : To pin 30 of uPD7759 sample ROM (A17 on a 27C040)
+ D1 : To A10 of color RAM
+ D0 : To A9 of color RAM
+ */
+ int newbank = data & 3;
+ if (newbank != m_palbank)
{
- /* I/O ports */
- case 0x00/2:
- case 0x02/2:
- case 0x04/2:
- case 0x06/2:
- case 0x08/2:
- case 0x0a/2:
- case 0x0c/2:
- case 0x0e/2:
- /* if the port is configured as an output, return the last thing written */
- if (m_misc_io_data[0x1e/2] & (1 << offset))
- return m_misc_io_data[offset];
-
- /* otherwise, return an input port */
- if (offset == 0x04/2 && m_sound_banks)
- return (ioport(portnames[offset])->read() & 0xbf) | (m_upd7759->busy_r() << 6);
- return ioport(portnames[offset])->read();
-
- /* 'SEGA' protection */
- case 0x10/2:
- return 'S';
- case 0x12/2:
- return 'E';
- case 0x14/2:
- return 'G';
- case 0x16/2:
- return 'A';
-
- /* CNT register & mirror */
- case 0x18/2:
- case 0x1c/2:
- return m_misc_io_data[0x1c/2];
-
- /* port direction register & mirror */
- case 0x1a/2:
- case 0x1e/2:
- return m_misc_io_data[0x1e/2];
+ //m_screen->update_partial(m_screen->vpos() + 1);
+ m_palbank = newbank;
+ recompute_palette_tables();
}
- return 0xffff;
-}
-
-
-WRITE16_MEMBER(segac2_state::io_chip_w )
-{
- UINT8 newbank;
-// UINT8 old;
-
- /* generic implementation */
- offset &= 0x1f/2;
-// old = m_misc_io_data[offset];
- m_misc_io_data[offset] = data;
-
- switch (offset)
+ if (m_sound_banks > 1)
{
- /* I/O ports */
- case 0x00/2:
- case 0x02/2:
- case 0x04/2:
- case 0x08/2:
- case 0x0a/2:
- case 0x0c/2:
- break;
-
- /* miscellaneous output */
- case 0x06/2:
- /*
- D7 : To pin 3 of JP15. (Watchdog clock control)
- D6 : To MUTE input pin on TDA1518BQ amplifier.
- D5 : To CN2 pin 10. (Unknown purpose)
- D4 : To CN2 pin 11. (Unknown purpose)
- D3 : To CN1 pin K. (Coin lockout 2)
- D2 : To CN1 pin 9. (Coin lockout 1)
- D1 : To CN1 pin J. (Coin meter 2)
- D0 : To CN1 pin 8. (Coin meter 1)
- */
-/* coin_lockout_w(space.machine(), 1, data & 0x08);
- coin_lockout_w(space.machine(), 0, data & 0x04); */
- coin_counter_w(space.machine(), 1, data & 0x02);
- coin_counter_w(space.machine(), 0, data & 0x01);
- break;
-
- /* banking */
- case 0x0e/2:
- /*
- D7 : To pin A19 of CN4
- D6 : To pin B19 of CN4
- D5 : ?
- D4 : ?
- D3 : To pin 31 of uPD7759 sample ROM (A18 on a 27C040)
- D2 : To pin 30 of uPD7759 sample ROM (A17 on a 27C040)
- D1 : To A10 of color RAM
- D0 : To A9 of color RAM
- */
- newbank = data & 3;
- if (newbank != m_palbank)
- {
- //m_screen->update_partial(m_screen->vpos() + 1);
- m_palbank = newbank;
- recompute_palette_tables();
- }
- if (m_sound_banks > 1)
- {
- newbank = (data >> 2) & (m_sound_banks - 1);
- m_upd7759->set_bank_base(newbank * 0x20000);
- }
- break;
-
- /* CNT register */
- case 0x1c/2:
- if (m_sound_banks > 1)
- {
- m_upd7759->reset_w((data >> 1) & 1);
- }
- break;
+ newbank = (data >> 2) & (m_sound_banks - 1);
+ m_upd7759->set_bank_base(newbank * 0x20000);
}
}
@@ -713,7 +634,7 @@ static ADDRESS_MAP_START( main_map, AS_PROGRAM, 16, segac2_state )
AM_RANGE(0x000000, 0x1fffff) AM_ROM
AM_RANGE(0x800000, 0x800001) AM_MIRROR(0x13fdfe) AM_READWRITE(prot_r, prot_w)
AM_RANGE(0x800200, 0x800201) AM_MIRROR(0x13fdfe) AM_WRITE(control_w)
- AM_RANGE(0x840000, 0x84001f) AM_MIRROR(0x13fee0) AM_READWRITE(io_chip_r, io_chip_w)
+ AM_RANGE(0x840000, 0x84001f) AM_MIRROR(0x13fee0) AM_DEVREADWRITE8("io", sega_315_5296_device, read, write, 0x00ff)
AM_RANGE(0x840100, 0x840107) AM_MIRROR(0x13fef8) AM_DEVREADWRITE8("ymsnd", ym3438_device, read, write, 0x00ff)
AM_RANGE(0x880100, 0x880101) AM_MIRROR(0x13fefe) AM_WRITE(counter_timer_w)
AM_RANGE(0x8c0000, 0x8c0fff) AM_MIRROR(0x13f000) AM_READWRITE(palette_r, palette_w) AM_SHARE("paletteram")
@@ -758,14 +679,6 @@ static INPUT_PORTS_START( systemc_generic )
PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(2)
PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(2)
- PORT_START("PORTC")
- PORT_BIT( 0x3f, IP_ACTIVE_LOW, IPT_UNUSED )
- PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_SPECIAL ) /* From uPD7759 pin 18. (/BUSY output) */
- PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_SPECIAL ) /* From MB3773P pin 1. (/RESET output) */
-
- PORT_START("PORTD")
- PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNUSED )
-
PORT_START("SERVICE")
PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_COIN1 )
PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_COIN2 )
@@ -788,9 +701,6 @@ static INPUT_PORTS_START( systemc_generic )
PORT_DIPUNUSED_DIPLOC( 0x20, IP_ACTIVE_LOW, "SW2:6" )
PORT_DIPUNUSED_DIPLOC( 0x40, IP_ACTIVE_LOW, "SW2:7" )
PORT_DIPUNUSED_DIPLOC( 0x80, IP_ACTIVE_LOW, "SW2:8" )
-
- PORT_START("PORTH")
- PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNUSED )
INPUT_PORTS_END
@@ -1465,6 +1375,17 @@ static MACHINE_CONFIG_START( segac, segac2_state )
MCFG_MACHINE_RESET_OVERRIDE(segac2_state,segac2)
MCFG_NVRAM_ADD_1FILL("nvram") // borencha requires 0xff fill or there is no sound (it lacks some of the init code of the borench set)
+ MCFG_DEVICE_ADD("io", SEGA_315_5296, 0)
+ MCFG_315_5296_IN_PORTA_CB(IOPORT("P1"))
+ MCFG_315_5296_IN_PORTB_CB(IOPORT("P2"))
+ MCFG_315_5296_IN_PORTC_CB(READ8(segac2_state, io_portc_r))
+ MCFG_315_5296_OUT_PORTD_CB(WRITE8(segac2_state, io_portd_w))
+ MCFG_315_5296_IN_PORTE_CB(IOPORT("SERVICE"))
+ MCFG_315_5296_IN_PORTF_CB(IOPORT("COINAGE"))
+ MCFG_315_5296_IN_PORTG_CB(IOPORT("DSW"))
+ MCFG_315_5296_OUT_PORTH_CB(WRITE8(segac2_state, io_porth_w))
+
+ /* video hardware */
MCFG_DEVICE_ADD("gen_vdp", SEGA315_5313, 0)
MCFG_SEGA315_5313_IS_PAL(false)
MCFG_SEGA315_5313_SND_IRQ_CALLBACK(WRITELINE(segac2_state, vdp_sndirqline_callback_c2));
@@ -1475,7 +1396,6 @@ static MACHINE_CONFIG_START( segac, segac2_state )
MCFG_TIMER_DEVICE_ADD_SCANLINE("scantimer", "gen_vdp", sega315_5313_device, megadriv_scanline_timer_callback_alt_timing, "megadriv", 0, 1)
-
MCFG_SCREEN_ADD("megadriv", RASTER)
MCFG_SCREEN_REFRESH_RATE(60)
MCFG_SCREEN_SIZE(512, 262)
@@ -1503,6 +1423,8 @@ MACHINE_CONFIG_END
static MACHINE_CONFIG_DERIVED( segac2, segac )
/* basic machine hardware */
+ MCFG_DEVICE_MODIFY("io")
+ MCFG_315_5296_OUT_CNT1_CB(DEVWRITELINE("upd", upd7759_device, reset_w))
/* sound hardware */
MCFG_SOUND_ADD("upd", UPD7759, XL1_CLOCK)
diff --git a/src/mame/machine/315-5296.c b/src/mame/machine/315-5296.c
new file mode 100644
index 00000000000..6104df5bf38
--- /dev/null
+++ b/src/mame/machine/315-5296.c
@@ -0,0 +1,178 @@
+/**********************************************************************
+
+ Sega 315-5296 I/O chip
+
+ Sega 100-pin QFP, with 8 bidirectional I/O ports, and 3 output pins.
+ Commonly used from the late 80s up until Sega Model 2.
+
+**********************************************************************/
+
+#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 clock)
+ : device_t(mconfig, SEGA_315_5296, "Sega 315-5296", 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 (int i = 0; i < 3; i++)
+ (*m_out_cnt_cb[i])(0);
+}
+
+
+//-------------------------------------------------
+
+READ8_MEMBER( sega_315_5296_device::read )
+{
+ offset &= 0xf;
+
+ 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;
+ }
+
+ return 0xff;
+}
+
+
+WRITE8_MEMBER( sega_315_5296_device::write )
+{
+ offset &= 0xf;
+
+ 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, other bits: ?
+ for (int i = 0; i < 3; i++)
+ (*m_out_cnt_cb[i])(data >> i & 1);
+
+ 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;
+ }
+}
diff --git a/src/mame/machine/315-5296.h b/src/mame/machine/315-5296.h
new file mode 100644
index 00000000000..a3882475db6
--- /dev/null
+++ b/src/mame/machine/315-5296.h
@@ -0,0 +1,145 @@
+/**********************************************************************
+
+ Sega 315-5296 I/O chip
+
+ Copyright MAME Team.
+ Visit http://mamedev.org for licensing and usage restrictions.
+
+**********************************************************************/
+
+#pragma once
+
+#ifndef _SEGA_315_5296_H
+#define _SEGA_315_5296_H
+
+#include "emu.h"
+
+
+//**************************************************************************
+// INTERFACE CONFIGURATION MACROS
+//**************************************************************************
+
+// A to H 8-bit input ports
+#define MCFG_315_5296_IN_PORTA_CB(_devcb) \
+ devcb = &sega_315_5296_device::set_in_pa_callback(*device, DEVCB_##_devcb);
+#define MCFG_315_5296_IN_PORTB_CB(_devcb) \
+ devcb = &sega_315_5296_device::set_in_pb_callback(*device, DEVCB_##_devcb);
+#define MCFG_315_5296_IN_PORTC_CB(_devcb) \
+ devcb = &sega_315_5296_device::set_in_pc_callback(*device, DEVCB_##_devcb);
+#define MCFG_315_5296_IN_PORTD_CB(_devcb) \
+ devcb = &sega_315_5296_device::set_in_pd_callback(*device, DEVCB_##_devcb);
+#define MCFG_315_5296_IN_PORTE_CB(_devcb) \
+ devcb = &sega_315_5296_device::set_in_pe_callback(*device, DEVCB_##_devcb);
+#define MCFG_315_5296_IN_PORTF_CB(_devcb) \
+ devcb = &sega_315_5296_device::set_in_pf_callback(*device, DEVCB_##_devcb);
+#define MCFG_315_5296_IN_PORTG_CB(_devcb) \
+ devcb = &sega_315_5296_device::set_in_pg_callback(*device, DEVCB_##_devcb);
+#define MCFG_315_5296_IN_PORTH_CB(_devcb) \
+ devcb = &sega_315_5296_device::set_in_ph_callback(*device, DEVCB_##_devcb);
+
+// A to H 8-bit output ports
+#define MCFG_315_5296_OUT_PORTA_CB(_devcb) \
+ devcb = &sega_315_5296_device::set_out_pa_callback(*device, DEVCB_##_devcb);
+#define MCFG_315_5296_OUT_PORTB_CB(_devcb) \
+ devcb = &sega_315_5296_device::set_out_pb_callback(*device, DEVCB_##_devcb);
+#define MCFG_315_5296_OUT_PORTC_CB(_devcb) \
+ devcb = &sega_315_5296_device::set_out_pc_callback(*device, DEVCB_##_devcb);
+#define MCFG_315_5296_OUT_PORTD_CB(_devcb) \
+ devcb = &sega_315_5296_device::set_out_pd_callback(*device, DEVCB_##_devcb);
+#define MCFG_315_5296_OUT_PORTE_CB(_devcb) \
+ devcb = &sega_315_5296_device::set_out_pe_callback(*device, DEVCB_##_devcb);
+#define MCFG_315_5296_OUT_PORTF_CB(_devcb) \
+ devcb = &sega_315_5296_device::set_out_pf_callback(*device, DEVCB_##_devcb);
+#define MCFG_315_5296_OUT_PORTG_CB(_devcb) \
+ devcb = &sega_315_5296_device::set_out_pg_callback(*device, DEVCB_##_devcb);
+#define MCFG_315_5296_OUT_PORTH_CB(_devcb) \
+ devcb = &sega_315_5296_device::set_out_ph_callback(*device, DEVCB_##_devcb);
+
+// CNT output pins
+#define MCFG_315_5296_OUT_CNT0_CB(_devcb) \
+ devcb = &sega_315_5296_device::set_out_cnt0_callback(*device, DEVCB_##_devcb);
+#define MCFG_315_5296_OUT_CNT1_CB(_devcb) \
+ devcb = &sega_315_5296_device::set_out_cnt1_callback(*device, DEVCB_##_devcb);
+#define MCFG_315_5296_OUT_CNT2_CB(_devcb) \
+ devcb = &sega_315_5296_device::set_out_cnt2_callback(*device, DEVCB_##_devcb);
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+// ======================> sega_315_5296_device
+
+class sega_315_5296_device : public device_t
+{
+public:
+ sega_315_5296_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // static configuration helpers
+ template<class _Object> static devcb_base &set_in_pa_callback(device_t &device, _Object object) { return downcast<sega_315_5296_device &>(device).m_in_pa_cb.set_callback(object); }
+ template<class _Object> static devcb_base &set_in_pb_callback(device_t &device, _Object object) { return downcast<sega_315_5296_device &>(device).m_in_pb_cb.set_callback(object); }
+ template<class _Object> static devcb_base &set_in_pc_callback(device_t &device, _Object object) { return downcast<sega_315_5296_device &>(device).m_in_pc_cb.set_callback(object); }
+ template<class _Object> static devcb_base &set_in_pd_callback(device_t &device, _Object object) { return downcast<sega_315_5296_device &>(device).m_in_pd_cb.set_callback(object); }
+ template<class _Object> static devcb_base &set_in_pe_callback(device_t &device, _Object object) { return downcast<sega_315_5296_device &>(device).m_in_pe_cb.set_callback(object); }
+ template<class _Object> static devcb_base &set_in_pf_callback(device_t &device, _Object object) { return downcast<sega_315_5296_device &>(device).m_in_pf_cb.set_callback(object); }
+ template<class _Object> static devcb_base &set_in_pg_callback(device_t &device, _Object object) { return downcast<sega_315_5296_device &>(device).m_in_pg_cb.set_callback(object); }
+ template<class _Object> static devcb_base &set_in_ph_callback(device_t &device, _Object object) { return downcast<sega_315_5296_device &>(device).m_in_ph_cb.set_callback(object); }
+
+ template<class _Object> static devcb_base &set_out_pa_callback(device_t &device, _Object object) { return downcast<sega_315_5296_device &>(device).m_out_pa_cb.set_callback(object); }
+ template<class _Object> static devcb_base &set_out_pb_callback(device_t &device, _Object object) { return downcast<sega_315_5296_device &>(device).m_out_pb_cb.set_callback(object); }
+ template<class _Object> static devcb_base &set_out_pc_callback(device_t &device, _Object object) { return downcast<sega_315_5296_device &>(device).m_out_pc_cb.set_callback(object); }
+ template<class _Object> static devcb_base &set_out_pd_callback(device_t &device, _Object object) { return downcast<sega_315_5296_device &>(device).m_out_pd_cb.set_callback(object); }
+ template<class _Object> static devcb_base &set_out_pe_callback(device_t &device, _Object object) { return downcast<sega_315_5296_device &>(device).m_out_pe_cb.set_callback(object); }
+ template<class _Object> static devcb_base &set_out_pf_callback(device_t &device, _Object object) { return downcast<sega_315_5296_device &>(device).m_out_pf_cb.set_callback(object); }
+ template<class _Object> static devcb_base &set_out_pg_callback(device_t &device, _Object object) { return downcast<sega_315_5296_device &>(device).m_out_pg_cb.set_callback(object); }
+ template<class _Object> static devcb_base &set_out_ph_callback(device_t &device, _Object object) { return downcast<sega_315_5296_device &>(device).m_out_ph_cb.set_callback(object); }
+
+ template<class _Object> static devcb_base &set_out_cnt0_callback(device_t &device, _Object object) { return downcast<sega_315_5296_device &>(device).m_out_cnt0_cb.set_callback(object); }
+ template<class _Object> static devcb_base &set_out_cnt1_callback(device_t &device, _Object object) { return downcast<sega_315_5296_device &>(device).m_out_cnt1_cb.set_callback(object); }
+ template<class _Object> static devcb_base &set_out_cnt2_callback(device_t &device, _Object object) { return downcast<sega_315_5296_device &>(device).m_out_cnt2_cb.set_callback(object); }
+
+ DECLARE_READ8_MEMBER( read );
+ DECLARE_WRITE8_MEMBER( write );
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+
+private:
+ devcb_read8 m_in_pa_cb;
+ devcb_read8 m_in_pb_cb;
+ devcb_read8 m_in_pc_cb;
+ devcb_read8 m_in_pd_cb;
+ devcb_read8 m_in_pe_cb;
+ devcb_read8 m_in_pf_cb;
+ devcb_read8 m_in_pg_cb;
+ devcb_read8 m_in_ph_cb;
+
+ devcb_write8 m_out_pa_cb;
+ devcb_write8 m_out_pb_cb;
+ devcb_write8 m_out_pc_cb;
+ devcb_write8 m_out_pd_cb;
+ devcb_write8 m_out_pe_cb;
+ devcb_write8 m_out_pf_cb;
+ devcb_write8 m_out_pg_cb;
+ devcb_write8 m_out_ph_cb;
+
+ devcb_write_line m_out_cnt0_cb;
+ devcb_write_line m_out_cnt1_cb;
+ devcb_write_line m_out_cnt2_cb;
+
+ devcb_read8 *m_in_port_cb[8];
+ devcb_write8 *m_out_port_cb[8];
+ devcb_write_line *m_out_cnt_cb[3];
+
+ UINT8 m_output_latch[8];
+ UINT8 m_cnt;
+ UINT8 m_dir;
+};
+
+// device type definition
+extern const device_type SEGA_315_5296;
+
+
+#endif /* _SEGA_315_5296_H */
diff --git a/src/mame/mame.mak b/src/mame/mame.mak
index ecaa2675fe2..200131b3832 100644
--- a/src/mame/mame.mak
+++ b/src/mame/mame.mak
@@ -1727,6 +1727,7 @@ $(MAMEOBJ)/sega.a: \
$(DRIVERS)/turbo.o $(AUDIO)/turbo.o $(VIDEO)/turbo.o \
$(DRIVERS)/vicdual.o $(AUDIO)/vicdual.o $(VIDEO)/vicdual.o $(AUDIO)/carnival.o $(AUDIO)/depthch.o $(AUDIO)/invinco.o $(AUDIO)/pulsar.o \
$(DRIVERS)/zaxxon.o $(AUDIO)/zaxxon.o $(VIDEO)/zaxxon.o \
+ $(MACHINE)/315-5296.o \
$(MACHINE)/fd1089.o \
$(MACHINE)/fd1094.o \
$(MACHINE)/fddebug.o \