text: // license:BSD-3-Clause
  // copyright-holders:Aaron Giles
  /***************************************************************************
  
      Atari CAGE Audio Board
  
  ****************************************************************************/
  
  
  #include "emu.h"
  #include "cage.h"
  #include "speaker.h"
  
  
  #define LOG_COMM            (0)
  #define LOG_32031_IOPORTS   (0)
  
  
  
  /*************************************
   *
   *  Constants/macros
   *
   *************************************/
  
  #define DAC_BUFFER_CHANNELS     4
  #define STACK_SOUND_BUFSIZE     (1024)
  
  /*************************************
   *
   *  I/O port definitions
   *
   *************************************/
  
  #define DMA_GLOBAL_CTL          0x00
  #define DMA_SOURCE_ADDR         0x04
  #define DMA_DEST_ADDR           0x06
  #define DMA_TRANSFER_COUNT      0x08
  
  #define TIMER0_GLOBAL_CTL       0x20
  #define TIMER0_COUNTER          0x24
  #define TIMER0_PERIOD           0x28
  
  #define TIMER1_GLOBAL_CTL       0x30
  #define TIMER1_COUNTER          0x34
  #define TIMER1_PERIOD           0x38
  
  #define SPORT_GLOBAL_CTL        0x40
  #define SPORT_TX_CTL            0x42
  #define SPORT_RX_CTL            0x43
  #define SPORT_TIMER_CTL         0x44
  #define SPORT_TIMER_COUNTER     0x45
  #define SPORT_TIMER_PERIOD      0x46
  #define SPORT_DATA_TX           0x48
  #define SPORT_DATA_RX           0x4c
  
  static const char *const register_names[] =
  {
  	"TMS32031-DMA global control", nullptr, nullptr, nullptr,
  	"TMS32031-DMA source address", nullptr, "TMS32031-DMA destination address", nullptr,
  	"TMS32031-DMA transfer counter", nullptr, nullptr, nullptr,
  	nullptr, nullptr, nullptr, nullptr,
  
  	nullptr, nullptr, nullptr, nullptr,
  	nullptr, nullptr, nullptr, nullptr,
  	nullptr, nullptr, nullptr, nullptr,
  	nullptr, nullptr, nullptr, nullptr,
  
  	"TMS32031-Timer 0 global control", nullptr, nullptr, nullptr,
  	"TMS32031-Timer 0 counter", nullptr, nullptr, nullptr,
  	"TMS32031-Timer 0 period", nullptr, nullptr, nullptr,
  	nullptr, nullptr, nullptr, nullptr,
  
  	"TMS32031-Timer 1 global control", nullptr, nullptr, nullptr,
  	"TMS32031-Timer 1 counter", nullptr, nullptr, nullptr,
  	"TMS32031-Timer 1 period", nullptr, nullptr, nullptr,
  	nullptr, nullptr, nullptr, nullptr,
  
  	"TMS32031-Serial port global control", nullptr, "TMS32031-Serial port TX control", "TMS32031-Serial port RX control",
  	"TMS32031-Serial port timer control", "TMS32031-Serial port timer counter", "TMS32031-Serial port timer period", nullptr,
  	"TMS32031-Serial port data TX", nullptr, nullptr, nullptr,
  	"TMS32031-Serial port data RX", nullptr, nullptr, nullptr,
  
  	nullptr, nullptr, nullptr, nullptr,
  	nullptr, nullptr, nullptr, nullptr,
  	nullptr, nullptr, nullptr, nullptr,
  	nullptr, nullptr, nullptr, nullptr,
  
  	nullptr, nullptr, nullptr, nullptr,
  	"TMS32031-Primary bus control", nullptr, nullptr, nullptr,
  	nullptr, nullptr, nullptr, nullptr,
  	nullptr, nullptr, nullptr, nullptr,
  
  	nullptr, nullptr, nullptr, nullptr,
  	nullptr, nullptr, nullptr, nullptr,
  	nullptr, nullptr, nullptr, nullptr,
  	nullptr, nullptr, nullptr, nullptr
  };
  
  
  
  /*************************************
   *
   *  Initialization
   *
   *************************************/
  
  DEFINE_DEVICE_TYPE(ATARI_CAGE, atari_cage_device, "atari_cage", "Atari CAGE")
  
  
  //-------------------------------------------------
  //  atari_cage_device - constructor
  //-------------------------------------------------
  
  atari_cage_device::atari_cage_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
  	atari_cage_device(mconfig, ATARI_CAGE, tag, owner, clock)
  {
  }
  
  atari_cage_device::atari_cage_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) :
  	device_t(mconfig, type, tag, owner, clock),
  	m_cpu(*this, "cpu"),
  	m_cageram(*this, "cageram"),
  	m_soundlatch(*this, "soundlatch"),
  	m_dma_timer(*this, "cage_dma_timer"),
  	m_timer(*this, "cage_timer%u", 0U),
  	m_dmadac(*this, "dac%u", 1U),
  	m_bootbank(*this, "bootbank"),
  	m_mainbank(*this, "mainbank"),
  	m_bootrom(*this, "boot"),
  	m_mainrom(*this, DEVICE_SELF),
  	m_irqhandler(*this)
  {
  }
  
  //-------------------------------------------------
  //  device_start - device-specific startup
  //-------------------------------------------------
  
  void atari_cage_device::device_start()
  {
  	attotime cage_cpu_clock_period;
  
  	// resolve callbacks
  	m_irqhandler.resolve_safe();
  
  	m_bootbank->set_base(m_bootrom->base());
  	m_mainbank->set_base(m_mainrom->base());
  
  	cage_cpu_clock_period = attotime::from_hz(m_cpu->clock());
  	m_cpu_h1_clock_period = cage_cpu_clock_period * 2;
  
  	if (m_speedup) {
  		m_cpu->space(AS_PROGRAM).install_write_handler(m_speedup, m_speedup, write32s_delegate(*this, FUNC(atari_cage_device::speedup_w)));
  		m_speedup_ram = m_cageram + m_speedup;
  	}
  
  	save_item(NAME(m_cpu_to_cage_ready));
  	save_item(NAME(m_cage_to_cpu_ready));
  	save_item(NAME(m_serial_period_per_word));
  	save_item(NAME(m_dma_enabled));
  	save_item(NAME(m_dma_timer_enabled));
  	save_item(NAME(m_timer_enabled));
  	save_item(NAME(m_from_main));
  	save_item(NAME(m_control));
  	save_item(NAME(m_tms32031_io_regs));
  }
  
  
  void atari_cage_device::reset_w(int state)
  {
  	if (state)
  		control_w(0);
  	m_cpu->set_input_line(INPUT_LINE_RESET, state ? ASSERT_LINE : CLEAR_LINE);
  }
  
  
  
  /*************************************
   *
   *  DMA timers
   *
   *************************************/
  
  TIMER_DEVICE_CALLBACK_MEMBER( atari_cage_device::dma_timer_callback )
  {
  	/* if we weren't enabled, don't do anything, just shut ourself off */
  	if (!m_dma_enabled)
  	{
  		if (m_dma_timer_enabled)
  		{
  			timer.adjust(attotime::never);
  			m_dma_timer_enabled = 0;
  		}
  		return;
  	}
  
  	/* set the final count to 0 and the source address to the final address */
  	m_tms32031_io_regs[DMA_TRANSFER_COUNT] = 0;
  	m_tms32031_io_regs[DMA_SOURCE_ADDR] = param;
  
  	/* set the interrupt */
  	m_cpu->set_input_line(TMS3203X_DINT0, ASSERT_LINE);
  	m_dma_enabled = 0;
  }
  
  
  void atari_cage_device::update_dma_state()
  {
  	/* determine the new enabled state */
  	int enabled = ((m_tms32031_io_regs[DMA_GLOBAL_CTL] & 3) == 3) && (m_tms32031_io_regs[DMA_TRANSFER_COUNT] != 0);
  
  	/* see if we turned on */
  	if (enabled && !m_dma_enabled)
  	{
  		int16_t sound_data[STACK_SOUND_BUFSIZE];
  		uint32_t addr, inc;
  		int i;
  
  		/* make sure our assumptions are correct */
  		if (m_tms32031_io_regs[DMA_DEST_ADDR] != 0x808048)
  			logerror("CAGE DMA: unexpected dest address %08X!\n", m_tms32031_io_regs[DMA_DEST_ADDR]);
  		if ((m_tms32031_io_regs[DMA_GLOBAL_CTL] & 0xfef) != 0xe03)
  			logerror("CAGE DMA: unexpected transfer params %08X!\n", m_tms32031_io_regs[DMA_GLOBAL_CTL]);
  
  		/* do the DMA up front */
  		addr = m_tms32031_io_regs[DMA_SOURCE_ADDR];
  		inc = (m_tms32031_io_regs[DMA_GLOBAL_CTL] >> 4) & 1;
  		for (i = 0; i < m_tms32031_io_regs[DMA_TRANSFER_COUNT]; i++)
  		{
  			sound_data[i % STACK_SOUND_BUFSIZE] = m_cpu->space(AS_PROGRAM).read_dword(addr);
  			addr += inc;
  			if (i % STACK_SOUND_BUFSIZE == STACK_SOUND_BUFSIZE - 1)
  				for (int j = 0; j < DAC_BUFFER_CHANNELS; j++)
  					m_dmadac[j]->transfer(j, 1, DAC_BUFFER_CHANNELS, STACK_SOUND_BUFSIZE / DAC_BUFFER_CHANNELS, sound_data);
  		}
  		if (m_tms32031_io_regs[DMA_TRANSFER_COUNT] % STACK_SOUND_BUFSIZE != 0)
  			for (int j = 0; j < DAC_BUFFER_CHANNELS; j++)
  				m_dmadac[j]->transfer(j, 1, DAC_BUFFER_CHANNELS, (m_tms32031_io_regs[DMA_TRANSFER_COUNT] % STACK_SOUND_BUFSIZE) / DAC_BUFFER_CHANNELS, sound_data);
  
  		/* compute the time of the interrupt and set the timer */
  		if (!m_dma_timer_enabled)
  		{
  			attotime period = m_serial_period_per_word * m_tms32031_io_regs[DMA_TRANSFER_COUNT];
  			m_dma_timer->adjust(period, addr, period);
  			m_dma_timer_enabled = 1;
  		}
  	}
  
  	/* see if we turned off */
  	else if (!enabled && m_dma_enabled)
  	{
  		m_dma_timer->reset();
  		m_dma_timer_enabled = 0;
  	}
  
  	/* set the new state */
  	m_dma_enabled = enabled;
  }
  
  
  
  /*************************************
   *
   *  Internal timers
   *
   *************************************/
  
  TIMER_DEVICE_CALLBACK_MEMBER( atari_cage_device::cage_timer_callback )
  {
  	int which = param;
  	/* set the interrupt */
  	m_cpu->set_input_line(TMS3203X_TINT0 + which, ASSERT_LINE);
  	m_timer_enabled[which] = 0;
  	update_timer(which);
  }
  
  
  void atari_cage_device::update_timer(int which)
  {
  	/* determine the new enabled state */
  	int base = 0x10 * which;
  	int enabled = ((m_tms32031_io_regs[base + TIMER0_GLOBAL_CTL] & 0xc0) == 0xc0);
  
  	/* see if we turned on */
  	if (enabled && !m_timer_enabled[which])
  	{
  		attotime period = m_cpu_h1_clock_period * (2 * m_tms32031_io_regs[base + TIMER0_PERIOD]);
  
  		/* make sure our assumptions are correct */
  		if (m_tms32031_io_regs[base + TIMER0_GLOBAL_CTL] != 0x2c1)
  			logerror("CAGE TIMER%d: unexpected timer config %08X!\n", which, m_tms32031_io_regs[base + TIMER0_GLOBAL_CTL]);
  
  		m_timer[which]->adjust(period, which);
  	}
  
  	/* see if we turned off */
  	else if (!enabled && m_timer_enabled[which])
  	{
  		m_timer[which]->adjust(attotime::never, which);
  	}
  
  	/* set the new state */
  	m_timer_enabled[which] = enabled;
  }
  
  
  
  /*************************************
   *
   *  Serial port I/O
   *
   *************************************/
  
  void atari_cage_device::update_serial()
  {
  	attotime serial_clock_period, bit_clock_period;
  	uint32_t freq;
  
  	/* we start out at half the H1 frequency (or 2x the H1 period) */
  	serial_clock_period = m_cpu_h1_clock_period * 2;
  
  	/* if we're in clock mode, muliply by another factor of 2 */
  	if (m_tms32031_io_regs[SPORT_GLOBAL_CTL] & 4)
  		serial_clock_period *= 2;
  
  	/* now multiply by the timer period */
  	if ((m_tms32031_io_regs[SPORT_TIMER_PERIOD] & 0xffff) == 0)
  		bit_clock_period = (m_tms32031_io_regs[SPORT_GLOBAL_CTL] & 4) ? serial_clock_period : attotime::never;
  	else
  		bit_clock_period = serial_clock_period * (m_tms32031_io_regs[SPORT_TIMER_PERIOD] & 0xffff);
  
  	/* and times the number of bits per sample */
  	m_serial_period_per_word = bit_clock_period * (8 * (((m_tms32031_io_regs[SPORT_GLOBAL_CTL] >> 18) & 3) + 1));
  
  	/* compute the step value to stretch this to the sample_rate */
  	freq = m_serial_period_per_word.as_hz() / DAC_BUFFER_CHANNELS;
  	if (freq > 0 && freq < 100000)
  	{
  		for (int i = 0; i < 4; i++)
  		{
  			m_dmadac[i]->set_frequency(freq);
  			m_dmadac[i]->enable(1);
  		}
  	}
  }
  
  
  
  /*************************************
   *
   *  Master read/write
   *
   *************************************/
  
  uint32_t atari_cage_device::tms32031_io_r(offs_t offset)
  {
  	uint16_t result = m_tms32031_io_regs[offset];
  
  	switch (offset)
  	{
  		case DMA_GLOBAL_CTL:
  			result = (result & ~0xc) | (m_dma_enabled ? 0xc : 0x0);
  			break;
  	}
  
  	if (LOG_32031_IOPORTS)
  		logerror("%s CAGE:%s read -> %08X\n", machine().describe_context(), register_names[offset & 0x7f], result);
  	return result;
  }
  
  
  void atari_cage_device::tms32031_io_w(offs_t offset, uint32_t data, uint32_t mem_mask)
  {
  	COMBINE_DATA(&m_tms32031_io_regs[offset]);
  
  	if (LOG_32031_IOPORTS)
  		logerror("%s CAGE:%s write = %08X\n", machine().describe_context(), register_names[offset & 0x7f], m_tms32031_io_regs[offset]);
  
  	switch (offset)
  	{
  		case DMA_GLOBAL_CTL:
  		case DMA_SOURCE_ADDR:
  		case DMA_DEST_ADDR:
  		case DMA_TRANSFER_COUNT:
  			update_dma_state();
  			break;
  
  		case TIMER0_GLOBAL_CTL:
  		case TIMER0_COUNTER:
  		case TIMER0_PERIOD:
  			update_timer(0);
  			break;
  
  		case TIMER1_GLOBAL_CTL:
  		case TIMER1_COUNTER:
  		case TIMER1_PERIOD:
  			update_timer(1);
  			break;
  
  		case SPORT_TX_CTL:
  		case SPORT_RX_CTL:
  		case SPORT_TIMER_COUNTER:
  		case SPORT_DATA_RX:
  			break;
  
  		case SPORT_DATA_TX:
  #if (DAC_BUFFER_CHANNELS == 4)
  			if (int(m_serial_period_per_word.as_hz()) == 22050*4 && (m_tms32031_io_regs[SPORT_RX_CTL] & 0xff) == 0x62)
  				m_tms32031_io_regs[SPORT_RX_CTL] ^= 0x800;
  #endif
  			break;
  
  		case SPORT_GLOBAL_CTL:
  		case SPORT_TIMER_CTL:
  		case SPORT_TIMER_PERIOD:
  			update_serial();
  			break;
  	}
  }
  
  
  
  /*************************************
   *
   *  External interfaces
   *
   *************************************/
  
  void atari_cage_device::update_control_lines()
  {
  	int val;
  
  	/* set the IRQ to the main CPU */
  	int reason = 0;
  
  	if ((m_control & 3) == 3 && !m_cpu_to_cage_ready)
  		reason |= CAGE_IRQ_REASON_BUFFER_EMPTY;
  	if ((m_control & 2) && m_cage_to_cpu_ready)
  		reason |= CAGE_IRQ_REASON_DATA_READY;
  
  	m_irqhandler(0, reason);
  	/* set the IOF input lines */
  	val = m_cpu->state_int(TMS3203X_IOF);
  	val &= ~0x88;
  	if (m_cpu_to_cage_ready) val |= 0x08;
  	if (m_cage_to_cpu_ready) val |= 0x80;
  	m_cpu->set_state_int(TMS3203X_IOF, val);
  }
  
  
  uint32_t atari_cage_device::cage_from_main_r()
  {
  	if (LOG_COMM)
  		logerror("%s CAGE read command = %04X\n", machine().describe_context(), m_from_main);
  	m_cpu_to_cage_ready = 0;
  	update_control_lines();
  	m_cpu->set_input_line(TMS3203X_IRQ0, CLEAR_LINE);
  	return m_from_main;
  }
  
  
  void atari_cage_device::cage_from_main_ack_w(uint32_t data)
  {
  	if (LOG_COMM)
  	{
  			logerror("%s CAGE ack command = %04X\n", machine().describe_context(), m_from_main);
  	}
  }
  
  
  void atari_cage_device::cage_to_main_w(uint32_t data)
  {
  	if (LOG_COMM)
  		logerror("%s Data from CAGE = %04X\n", machine().describe_context(), data);
  	m_soundlatch->write(data);
  	m_cage_to_cpu_ready = 1;
  	update_control_lines();
  }
  
  
  uint32_t atari_cage_device::cage_io_status_r()
  {
  	int result = 0;
  	if (m_cpu_to_cage_ready)
  		result |= 0x80;
  	if (!m_cage_to_cpu_ready)
  		result |= 0x40;
  	return result;
  }
  
  
  uint16_t atari_cage_device::main_r()
  {
  	if (LOG_COMM)
  		logerror("%s:main read data = %04X\n", machine().describe_context(), m_soundlatch->read());
  	m_cage_to_cpu_ready = 0;
  	update_control_lines();
  	return m_soundlatch->read();
  }
  
  
  TIMER_CALLBACK_MEMBER( atari_cage_device::cage_deferred_w )
  {
  	m_from_main = param;
  	m_cpu_to_cage_ready = 1;
  	update_control_lines();
  	m_cpu->set_input_line(TMS3203X_IRQ0, ASSERT_LINE);
  }
  
  
  void atari_cage_device::main_w(uint16_t data)
  {
  	if (LOG_COMM)
  		logerror("%s:Command to CAGE = %04X\n", machine().describe_context(), data);
  	machine().scheduler().synchronize(timer_expired_delegate(FUNC(atari_cage_device::cage_deferred_w),this), data);
  }
  
  
  uint16_t atari_cage_device::control_r()
  {
  	uint16_t result = 0;
  
  	if (m_cpu_to_cage_ready)
  		result |= 2;
  	if (m_cage_to_cpu_ready)
  		result |= 1;
  
  	return result;
  }
  
  
  void atari_cage_device::control_w(uint16_t data)
  {
  	m_control = data;
  
  	/* CPU is reset if both control lines are 0 */
  	if (!(m_control & 3))
  	{
  		m_cpu->set_input_line(INPUT_LINE_RESET, ASSERT_LINE);
  		m_cpu->set_input_line(TMS3203X_IRQ1, ASSERT_LINE);
  
  		m_dma_enabled = 0;
  		m_dma_timer_enabled = 0;
  		m_dma_timer->reset();
  
  		m_timer_enabled[0] = 0;
  		m_timer_enabled[1] = 0;
  		m_timer[0]->reset();
  		m_timer[1]->reset();
  
  		memset(m_tms32031_io_regs, 0, 0x60 * 4);
  
  		m_cpu_to_cage_ready = 0;
  		m_cage_to_cpu_ready = 0;
  	}
  	else
  	{
  		m_cpu->set_input_line(INPUT_LINE_RESET, CLEAR_LINE);
  		m_cpu->set_input_line(TMS3203X_IRQ1, CLEAR_LINE);
  	}
  
  	/* update the control state */
  	update_control_lines();
  }
  
  
  
  /*************************************
   *
   *  Speedups
   *
   *************************************/
  
  void atari_cage_device::speedup_w(offs_t offset, uint32_t data, uint32_t mem_mask)
  {
  	m_cpu->eat_cycles(100);
  	COMBINE_DATA(&m_speedup_ram[offset]);
  }
  
  
  
  /*************************************
   *
   *  CPU memory map & config
   *
   *************************************/
  
  void atari_cage_device::cage_map(address_map &map)
  {
  	map(0x000000, 0x00ffff).ram().share("cageram");
  	map(0x200000, 0x200000).nopw();
  	map(0x400000, 0x47ffff).bankr("bootbank");
  	map(0x808000, 0x8080ff).rw(FUNC(atari_cage_device::tms32031_io_r), FUNC(atari_cage_device::tms32031_io_w));
  	map(0xa00000, 0xa00000).rw(FUNC(atari_cage_device::cage_from_main_r), FUNC(atari_cage_device::cage_to_main_w));
  	map(0xc00000, 0xffffff).bankr("mainbank");
  }
  
  
  void atari_cage_seattle_device::cage_map_seattle(address_map &map)
  {
  	map(0x000000, 0x00ffff).ram().share("cageram");
  	map(0x200000, 0x200000).nopw();
  	map(0x400000, 0x47ffff).bankr("bootbank");
  	map(0x808000, 0x8080ff).rw(FUNC(atari_cage_seattle_device::tms32031_io_r), FUNC(atari_cage_seattle_device::tms32031_io_w));
  	map(0xa00000, 0xa00000).rw(FUNC(atari_cage_seattle_device::cage_from_main_r), FUNC(atari_cage_seattle_device::cage_from_main_ack_w));
  	map(0xa00001, 0xa00001).w(FUNC(atari_cage_seattle_device::cage_to_main_w));
  	map(0xa00003, 0xa00003).r(FUNC(atari_cage_seattle_device::cage_io_status_r));
  	map(0xc00000, 0xffffff).bankr("mainbank");
  }
  
  
  //-------------------------------------------------
  //  machine_add_config - add device configuration
  //-------------------------------------------------
  
  void atari_cage_device::device_add_mconfig(machine_config &config)
  {
  	/* basic machine hardware */
  	TMS32031(config, m_cpu, 33868800);
  	m_cpu->set_addrmap(AS_PROGRAM, &atari_cage_device::cage_map);
  	m_cpu->set_mcbl_mode(true);
  
  	TIMER(config, m_dma_timer).configure_generic(DEVICE_SELF, FUNC(atari_cage_device::dma_timer_callback));
  	TIMER(config, m_timer[0]).configure_generic(DEVICE_SELF, FUNC(atari_cage_device::cage_timer_callback));
  	TIMER(config, m_timer[1]).configure_generic(DEVICE_SELF, FUNC(atari_cage_device::cage_timer_callback));
  
  	/* sound hardware */
  	SPEAKER(config, "lspeaker").front_left();
  	SPEAKER(config, "rspeaker").front_right();
  
  	GENERIC_LATCH_16(config, m_soundlatch);
  
  #if (DAC_BUFFER_CHANNELS == 4)
  	DMADAC(config, m_dmadac[0]).add_route(ALL_OUTPUTS, "rspeaker", 0.50);
  
  	DMADAC(config, m_dmadac[1]).add_route(ALL_OUTPUTS, "lspeaker", 0.50);
  
  	DMADAC(config, m_dmadac[2]).add_route(ALL_OUTPUTS, "lspeaker", 0.50);
  
  	DMADAC(config, m_dmadac[3]).add_route(ALL_OUTPUTS, "rspeaker", 0.50);
  #else
  	DMADAC(config, m_dmadac[0]).add_route(ALL_OUTPUTS, "lspeaker", 1.0);
  
  	DMADAC(config, m_dmadac[1]).add_route(ALL_OUTPUTS, "rspeaker", 1.0);
  #endif
  }
  
  
  DEFINE_DEVICE_TYPE(ATARI_CAGE_SEATTLE, atari_cage_seattle_device, "atari_cage_seattle", "Atari CAGE Seattle")
  
  
  //-------------------------------------------------
  //  atari_cage_seattle_device - constructor
  //-------------------------------------------------
  
  atari_cage_seattle_device::atari_cage_seattle_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
  	atari_cage_device(mconfig, ATARI_CAGE_SEATTLE, tag, owner, clock)
  {
  }
  
  
  //-------------------------------------------------
  //  machine_add_config - add device configuration
  //-------------------------------------------------
  
  
  void atari_cage_seattle_device::device_add_mconfig(machine_config &config)
  {
  	atari_cage_device::device_add_mconfig(config);
  
  	m_cpu->set_addrmap(AS_PROGRAM, &atari_cage_seattle_device::cage_map_seattle);
  }
  