diff options
Diffstat (limited to 'src/mame/audio/cage.cpp')
-rw-r--r-- | src/mame/audio/cage.cpp | 695 |
1 files changed, 695 insertions, 0 deletions
diff --git a/src/mame/audio/cage.cpp b/src/mame/audio/cage.cpp new file mode 100644 index 00000000000..7253f46de74 --- /dev/null +++ b/src/mame/audio/cage.cpp @@ -0,0 +1,695 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles +/*************************************************************************** + + Atari CAGE Audio Board + +****************************************************************************/ + + +#define LOG_COMM (0) +#define LOG_32031_IOPORTS (0) + + +#include "emu.h" +#include "cpu/tms32031/tms32031.h" +#include "cage.h" + + + +/************************************* + * + * 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", NULL, NULL, NULL, + "TMS32031-DMA source address", NULL, "TMS32031-DMA destination address", NULL, + "TMS32031-DMA transfer counter", NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + + "TMS32031-Timer 0 global control", NULL, NULL, NULL, + "TMS32031-Timer 0 counter", NULL, NULL, NULL, + "TMS32031-Timer 0 period", NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + + "TMS32031-Timer 1 global control", NULL, NULL, NULL, + "TMS32031-Timer 1 counter", NULL, NULL, NULL, + "TMS32031-Timer 1 period", NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + + "TMS32031-Serial port global control", NULL, "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", NULL, + "TMS32031-Serial port data TX", NULL, NULL, NULL, + "TMS32031-Serial port data RX", NULL, NULL, NULL, + + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + + NULL, NULL, NULL, NULL, + "TMS32031-Primary bus control", NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL +}; + + + +/************************************* + * + * Initialization + * + *************************************/ + +const device_type ATARI_CAGE = &device_creator<atari_cage_device>; + + +//------------------------------------------------- +// atari_cage_device - constructor +//------------------------------------------------- + +atari_cage_device::atari_cage_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : + device_t(mconfig, ATARI_CAGE, "Atari CAGE", tag, owner, clock, "atari_cage", __FILE__), + m_irqhandler(*this) +{ +} + +atari_cage_device::atari_cage_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source) : + device_t(mconfig, type, name, tag, owner, clock, shortname, source), + m_irqhandler(*this) +{ +} + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void atari_cage_device::device_start() +{ + attotime cage_cpu_clock_period; + int chan; + + // resolve callbacks + m_irqhandler.resolve_safe(); + + membank("bank10")->set_base(machine().root_device().memregion("cageboot")->base()); + membank("bank11")->set_base(machine().root_device().memregion("cage")->base()); + + m_cpu = subdevice<cpu_device>("cage"); + cage_cpu_clock_period = attotime::from_hz(m_cpu->clock()); + m_cpu_h1_clock_period = cage_cpu_clock_period * 2; + + m_dma_timer = subdevice<timer_device>("cage_dma_timer"); + m_timer[0] = subdevice<timer_device>("cage_timer0"); + m_timer[1] = subdevice<timer_device>("cage_timer1"); + + if (m_speedup) + m_speedup_ram = m_cpu->space(AS_PROGRAM).install_write_handler(m_speedup, m_speedup, write32_delegate(FUNC(atari_cage_device::speedup_w),this)); + + for (chan = 0; chan < DAC_BUFFER_CHANNELS; chan++) + { + char buffer[10]; + sprintf(buffer, "dac%d", chan + 1); + m_dmadac[chan] = subdevice<dmadac_sound_device>(buffer); + } + + 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)); +} + + +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 ) +{ + UINT32 *tms32031_io_regs = m_tms32031_io_regs; + + /* 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 */ + tms32031_io_regs[DMA_TRANSFER_COUNT] = 0; + tms32031_io_regs[DMA_SOURCE_ADDR] = param; + + /* set the interrupt */ + m_cpu->set_input_line(TMS3203X_DINT, ASSERT_LINE); + m_dma_enabled = 0; +} + + +void atari_cage_device::update_dma_state(address_space &space) +{ + UINT32 *tms32031_io_regs = m_tms32031_io_regs; + + /* determine the new enabled state */ + int enabled = ((tms32031_io_regs[DMA_GLOBAL_CTL] & 3) == 3) && (tms32031_io_regs[DMA_TRANSFER_COUNT] != 0); + + /* see if we turned on */ + if (enabled && !m_dma_enabled) + { + INT16 sound_data[STACK_SOUND_BUFSIZE]; + UINT32 addr, inc; + int i; + + /* make sure our assumptions are correct */ + if (tms32031_io_regs[DMA_DEST_ADDR] != 0x808048) + logerror("CAGE DMA: unexpected dest address %08X!\n", tms32031_io_regs[DMA_DEST_ADDR]); + if ((tms32031_io_regs[DMA_GLOBAL_CTL] & 0xfef) != 0xe03) + logerror("CAGE DMA: unexpected transfer params %08X!\n", tms32031_io_regs[DMA_GLOBAL_CTL]); + + /* do the DMA up front */ + addr = tms32031_io_regs[DMA_SOURCE_ADDR]; + inc = (tms32031_io_regs[DMA_GLOBAL_CTL] >> 4) & 1; + for (i = 0; i < tms32031_io_regs[DMA_TRANSFER_COUNT]; i++) + { + sound_data[i % STACK_SOUND_BUFSIZE] = space.read_dword(addr * 4); + addr += inc; + if (i % STACK_SOUND_BUFSIZE == STACK_SOUND_BUFSIZE - 1) + dmadac_transfer(&m_dmadac[0], DAC_BUFFER_CHANNELS, 1, DAC_BUFFER_CHANNELS, STACK_SOUND_BUFSIZE / DAC_BUFFER_CHANNELS, sound_data); + } + if (tms32031_io_regs[DMA_TRANSFER_COUNT] % STACK_SOUND_BUFSIZE != 0) + dmadac_transfer(&m_dmadac[0], DAC_BUFFER_CHANNELS, 1, DAC_BUFFER_CHANNELS, (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 * 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) +{ + UINT32 *tms32031_io_regs = m_tms32031_io_regs; + + /* determine the new enabled state */ + int base = 0x10 * which; + int enabled = ((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 * tms32031_io_regs[base + TIMER0_PERIOD]); + + /* make sure our assumptions are correct */ + if (tms32031_io_regs[base + TIMER0_GLOBAL_CTL] != 0x2c1) + logerror("CAGE TIMER%d: unexpected timer config %08X!\n", which, 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() +{ + UINT32 *tms32031_io_regs = m_tms32031_io_regs; + attotime serial_clock_period, bit_clock_period; + UINT32 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 (tms32031_io_regs[SPORT_GLOBAL_CTL] & 4) + serial_clock_period *= 2; + + /* now multiply by the timer period */ + bit_clock_period = serial_clock_period * (tms32031_io_regs[SPORT_TIMER_PERIOD] & 0xffff); + + /* and times the number of bits per sample */ + m_serial_period_per_word = bit_clock_period * (8 * (((tms32031_io_regs[SPORT_GLOBAL_CTL] >> 18) & 3) + 1)); + + /* compute the step value to stretch this to the sample_rate */ + freq = ATTOSECONDS_TO_HZ(m_serial_period_per_word.attoseconds()) / DAC_BUFFER_CHANNELS; + if (freq > 0 && freq < 100000) + { + dmadac_set_frequency(&m_dmadac[0], DAC_BUFFER_CHANNELS, freq); + dmadac_enable(&m_dmadac[0], DAC_BUFFER_CHANNELS, 1); + } +} + + + +/************************************* + * + * Master read/write + * + *************************************/ + +READ32_MEMBER( atari_cage_device::tms32031_io_r ) +{ + UINT32 *tms32031_io_regs = m_tms32031_io_regs; + UINT16 result = tms32031_io_regs[offset]; + + switch (offset) + { + case DMA_GLOBAL_CTL: + result = (result & ~0xc) | (m_dma_enabled ? 0xc : 0x0); + break; + } + + if (LOG_32031_IOPORTS) + logerror("CAGE:%06X:%s read -> %08X\n", space.device().safe_pc(), register_names[offset & 0x7f], result); + return result; +} + + +WRITE32_MEMBER( atari_cage_device::tms32031_io_w ) +{ + UINT32 *tms32031_io_regs = m_tms32031_io_regs; + + COMBINE_DATA(&tms32031_io_regs[offset]); + + if (LOG_32031_IOPORTS) + logerror("CAGE:%06X:%s write = %08X\n", space.device().safe_pc(), register_names[offset & 0x7f], 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(space); + 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)ATTOSECONDS_TO_HZ(m_serial_period_per_word.attoseconds()) == 22050*4 && (tms32031_io_regs[SPORT_RX_CTL] & 0xff) == 0x62) + 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(machine().driver_data()->generic_space(), 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); +} + + +READ32_MEMBER( atari_cage_device::cage_from_main_r ) +{ + if (LOG_COMM) + logerror("%06X:CAGE read command = %04X\n", space.device().safe_pc(), 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; +} + + +WRITE32_MEMBER( atari_cage_device::cage_from_main_ack_w ) +{ + if (LOG_COMM) + { + logerror("%06X:CAGE ack command = %04X\n", space.device().safe_pc(), m_from_main); + } +} + + +WRITE32_MEMBER( atari_cage_device::cage_to_main_w ) +{ + if (LOG_COMM) + logerror("%06X:Data from CAGE = %04X\n", space.device().safe_pc(), data); + driver_device *drvstate = space.machine().driver_data<driver_device>(); + drvstate->soundlatch_word_w(space, 0, data, mem_mask); + m_cage_to_cpu_ready = 1; + update_control_lines(); +} + + +READ32_MEMBER( 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 atari_cage_device::main_r() +{ + driver_device *drvstate = machine().driver_data<driver_device>(); + if (LOG_COMM) + logerror("%s:main read data = %04X\n", machine().describe_context(), drvstate->soundlatch_word_r(drvstate->generic_space(), 0, 0)); + m_cage_to_cpu_ready = 0; + update_control_lines(); + return drvstate->soundlatch_word_r(drvstate->generic_space(), 0, 0xffff); +} + + +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 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 atari_cage_device::control_r() +{ + UINT16 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 data) +{ + UINT32 *tms32031_io_regs = m_tms32031_io_regs; + + 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(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 + * + *************************************/ + +WRITE32_MEMBER( atari_cage_device::speedup_w ) +{ + space.device().execute().eat_cycles(100); + COMBINE_DATA(&m_speedup_ram[offset]); +} + + + +/************************************* + * + * CPU memory map & config + * + *************************************/ + +static ADDRESS_MAP_START( cage_map, AS_PROGRAM, 32, atari_cage_device ) + AM_RANGE(0x000000, 0x00ffff) AM_RAM + AM_RANGE(0x200000, 0x200000) AM_WRITENOP + AM_RANGE(0x400000, 0x47ffff) AM_ROMBANK("bank10") + AM_RANGE(0x808000, 0x8080ff) AM_READWRITE(tms32031_io_r, tms32031_io_w) + AM_RANGE(0x809800, 0x809fff) AM_RAM + AM_RANGE(0xa00000, 0xa00000) AM_READWRITE(cage_from_main_r, cage_to_main_w) + AM_RANGE(0xc00000, 0xffffff) AM_ROMBANK("bank11") +ADDRESS_MAP_END + + +static ADDRESS_MAP_START( cage_map_seattle, AS_PROGRAM, 32, atari_cage_seattle_device ) + AM_RANGE(0x000000, 0x00ffff) AM_RAM + AM_RANGE(0x200000, 0x200000) AM_WRITENOP + AM_RANGE(0x400000, 0x47ffff) AM_ROMBANK("bank10") + AM_RANGE(0x808000, 0x8080ff) AM_READWRITE(tms32031_io_r, tms32031_io_w) + AM_RANGE(0x809800, 0x809fff) AM_RAM + AM_RANGE(0xa00000, 0xa00000) AM_READWRITE(cage_from_main_r, cage_from_main_ack_w) + AM_RANGE(0xa00001, 0xa00001) AM_WRITE(cage_to_main_w) + AM_RANGE(0xa00003, 0xa00003) AM_READ(cage_io_status_r) + AM_RANGE(0xc00000, 0xffffff) AM_ROMBANK("bank11") +ADDRESS_MAP_END + + + +/************************************* + * + * CAGE machine driver + * + *************************************/ + +MACHINE_CONFIG_FRAGMENT( cage ) + + /* basic machine hardware */ + MCFG_CPU_ADD("cage", TMS32031, 33868800) + MCFG_CPU_PROGRAM_MAP(cage_map) + MCFG_TMS3203X_MCBL(true) + + MCFG_TIMER_DEVICE_ADD("cage_dma_timer", DEVICE_SELF, atari_cage_device, dma_timer_callback) + MCFG_TIMER_DEVICE_ADD("cage_timer0", DEVICE_SELF, atari_cage_device, cage_timer_callback) + MCFG_TIMER_DEVICE_ADD("cage_timer1", DEVICE_SELF, atari_cage_device, cage_timer_callback) + + /* sound hardware */ + MCFG_SPEAKER_STANDARD_STEREO("lspeaker", "rspeaker") + +#if (DAC_BUFFER_CHANNELS == 4) + MCFG_SOUND_ADD("dac1", DMADAC, 0) + MCFG_SOUND_ROUTE(ALL_OUTPUTS, "rspeaker", 0.50) + + MCFG_SOUND_ADD("dac2", DMADAC, 0) + MCFG_SOUND_ROUTE(ALL_OUTPUTS, "lspeaker", 0.50) + + MCFG_SOUND_ADD("dac3", DMADAC, 0) + MCFG_SOUND_ROUTE(ALL_OUTPUTS, "lspeaker", 0.50) + + MCFG_SOUND_ADD("dac4", DMADAC, 0) + MCFG_SOUND_ROUTE(ALL_OUTPUTS, "rspeaker", 0.50) +#else + MCFG_SOUND_ADD("dac1", DMADAC, 0) + MCFG_SOUND_ROUTE(ALL_OUTPUTS, "lspeaker", 1.0) + + MCFG_SOUND_ADD("dac2", DMADAC, 0) + MCFG_SOUND_ROUTE(ALL_OUTPUTS, "rspeaker", 1.0) +#endif +MACHINE_CONFIG_END + + +MACHINE_CONFIG_DERIVED( cage_seattle, cage ) + + MCFG_CPU_MODIFY("cage") + MCFG_CPU_PROGRAM_MAP(cage_map_seattle) +MACHINE_CONFIG_END + +//------------------------------------------------- +// machine_config_additions - device-specific +// machine configurations +//------------------------------------------------- + +machine_config_constructor atari_cage_device::device_mconfig_additions() const +{ + return MACHINE_CONFIG_NAME( cage ); +} + + +const device_type ATARI_CAGE_SEATTLE = &device_creator<atari_cage_seattle_device>; + + +//------------------------------------------------- +// atari_cage_seattle_device - constructor +//------------------------------------------------- + +atari_cage_seattle_device::atari_cage_seattle_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : + atari_cage_device(mconfig, ATARI_CAGE_SEATTLE, "Atari CAGE Seattle", tag, owner, clock, "atari_cage_seattle", __FILE__) +{ +} + +machine_config_constructor atari_cage_seattle_device::device_mconfig_additions() const +{ + return MACHINE_CONFIG_NAME( cage_seattle ); +} |