summaryrefslogtreecommitdiffstatshomepage
path: root/trunk/src/mame/audio/redalert.c
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/src/mame/audio/redalert.c')
-rw-r--r--trunk/src/mame/audio/redalert.c423
1 files changed, 423 insertions, 0 deletions
diff --git a/trunk/src/mame/audio/redalert.c b/trunk/src/mame/audio/redalert.c
new file mode 100644
index 00000000000..b12c59a68a7
--- /dev/null
+++ b/trunk/src/mame/audio/redalert.c
@@ -0,0 +1,423 @@
+/***************************************************************************
+
+ Irem Red Alert hardware
+
+ If you have any questions about how this driver works, don't hesitate to
+ ask. - Mike Balfour (mab22@po.cwru.edu)
+
+****************************************************************************/
+
+#include "emu.h"
+#include "cpu/m6800/m6800.h"
+#include "cpu/m6502/m6502.h"
+#include "machine/rescap.h"
+#include "cpu/i8085/i8085.h"
+#include "machine/6821pia.h"
+#include "sound/ay8910.h"
+#include "sound/hc55516.h"
+#include "includes/redalert.h"
+
+
+
+#define REDALERT_AUDIO_PCB_CLOCK (XTAL_12_5MHz)
+#define REDALERT_AUDIO_CPU_CLOCK (REDALERT_AUDIO_PCB_CLOCK / 12)
+#define REDALERT_AY8910_CLOCK (REDALERT_AUDIO_PCB_CLOCK / 6)
+#define REDALERT_AUDIO_CPU_IRQ_FREQ (1000000000 / PERIOD_OF_555_ASTABLE_NSEC(RES_K(120), RES_K(2.7), CAP_U(0.01)))
+
+#define REDALERT_VOICE_PCB_CLOCK (XTAL_6MHz)
+#define REDALERT_VOICE_CPU_CLOCK (REDALERT_VOICE_PCB_CLOCK)
+#define REDALERT_HC55516_CLOCK (REDALERT_VOICE_PCB_CLOCK / 256)
+
+#define DEMONEYE_AUDIO_PCB_CLOCK (XTAL_3_579545MHz)
+#define DEMONEYE_AUDIO_CPU_CLOCK (DEMONEYE_AUDIO_PCB_CLOCK / 4) /* what's the real divisor? */
+#define DEMONEYE_AY8910_CLOCK (DEMONEYE_AUDIO_PCB_CLOCK / 2) /* what's the real divisor? */
+
+
+
+/*************************************
+ *
+ * Read Alert analog sounds
+ *
+ *************************************/
+
+static WRITE8_DEVICE_HANDLER( redalert_analog_w )
+{
+ /* this port triggers analog sounds
+ D0 = Formation Aircraft?
+ D1 = Dive bombers?
+ D2 = Helicopters?
+ D3 = Launcher firing?
+ D4 = Explosion #1?
+ D5 = Explosion #2?
+ D6 = Explosion #3? */
+
+ logerror("Analog: %02X\n",data);
+}
+
+
+
+/*************************************
+ *
+ * Red Alert audio board
+ *
+ *************************************/
+
+WRITE8_HANDLER( redalert_audio_command_w )
+{
+ /* the byte is connected to port A of the AY8910 */
+ soundlatch_w(space, 0, data);
+
+ /* D7 is also connected to the NMI input of the CPU -
+ the NMI is actually toggled by a 74121 */
+ if ((data & 0x80) == 0x00)
+ cputag_set_input_line(space->machine(), "audiocpu", INPUT_LINE_NMI, PULSE_LINE);
+}
+
+
+static WRITE8_DEVICE_HANDLER( redalert_AY8910_w )
+{
+ redalert_state *state = device->machine().driver_data<redalert_state>();
+ /* BC2 is connected to a pull-up resistor, so BC2=1 always */
+ switch (data & 0x03)
+ {
+ /* BC1=0, BDIR=0 : inactive */
+ case 0x00:
+ break;
+
+ /* BC1=1, BDIR=0 : read from PSG */
+ case 0x01:
+ state->m_ay8910_latch_1 = ay8910_r(device, 0);
+ break;
+
+ /* BC1=0, BDIR=1 : write to PSG */
+ /* BC1=1, BDIR=1 : latch address */
+ case 0x02:
+ case 0x03:
+ default:
+ ay8910_data_address_w(device, data, state->m_ay8910_latch_2);
+ break;
+ }
+}
+
+
+static READ8_HANDLER( redalert_ay8910_latch_1_r )
+{
+ redalert_state *state = space->machine().driver_data<redalert_state>();
+ return state->m_ay8910_latch_1;
+}
+
+
+static WRITE8_HANDLER( redalert_ay8910_latch_2_w )
+{
+ redalert_state *state = space->machine().driver_data<redalert_state>();
+ state->m_ay8910_latch_2 = data;
+}
+
+
+static const ay8910_interface redalert_ay8910_interface =
+{
+ AY8910_LEGACY_OUTPUT,
+ AY8910_DEFAULT_LOADS,
+ DEVCB_MEMORY_HANDLER("audiocpu", PROGRAM, soundlatch_r),
+ DEVCB_NULL, /* port A/B read */
+ DEVCB_NULL,
+ DEVCB_HANDLER(redalert_analog_w) /* port A/B write */
+};
+
+
+static ADDRESS_MAP_START( redalert_audio_map, AS_PROGRAM, 8 )
+ ADDRESS_MAP_GLOBAL_MASK(0x7fff)
+ AM_RANGE(0x0000, 0x03ff) AM_MIRROR(0x0c00) AM_RAM
+ AM_RANGE(0x1000, 0x1000) AM_MIRROR(0x0ffe) AM_READNOP AM_DEVWRITE("aysnd", redalert_AY8910_w)
+ AM_RANGE(0x1001, 0x1001) AM_MIRROR(0x0ffe) AM_READWRITE(redalert_ay8910_latch_1_r, redalert_ay8910_latch_2_w)
+ AM_RANGE(0x2000, 0x6fff) AM_NOP
+ AM_RANGE(0x7000, 0x77ff) AM_MIRROR(0x0800) AM_ROM
+ADDRESS_MAP_END
+
+/*************************************
+ *
+ * Red Alert audio board
+ *
+ *************************************/
+
+static SOUND_START( redalert_audio )
+{
+ redalert_state *state = machine.driver_data<redalert_state>();
+ state->save_item(NAME(state->m_ay8910_latch_1));
+ state->save_item(NAME(state->m_ay8910_latch_2));
+}
+
+
+/*************************************
+ *
+ * Red Alert voice board
+ *
+ *************************************/
+
+WRITE8_HANDLER( redalert_voice_command_w )
+{
+ soundlatch2_w(space, 0, (data & 0x78) >> 3);
+ cputag_set_input_line(space->machine(), "voice", I8085_RST75_LINE, (~data & 0x80) ? ASSERT_LINE : CLEAR_LINE);
+}
+
+
+static WRITE_LINE_DEVICE_HANDLER( sod_callback )
+{
+ hc55516_digit_w(device->machine().device("cvsd"), state);
+}
+
+
+static READ_LINE_DEVICE_HANDLER( sid_callback )
+{
+ return hc55516_clock_state_r(device->machine().device("cvsd"));
+}
+
+
+static I8085_CONFIG( redalert_voice_i8085_config )
+{
+ DEVCB_NULL, /* STATUS changed callback */
+ DEVCB_NULL, /* INTE changed callback */
+ DEVCB_LINE(sid_callback), /* SID changed callback (8085A only) */
+ DEVCB_LINE(sod_callback) /* SOD changed callback (8085A only) */
+};
+
+
+static ADDRESS_MAP_START( redalert_voice_map, AS_PROGRAM, 8 )
+ AM_RANGE(0x0000, 0x3fff) AM_ROM
+ AM_RANGE(0x4000, 0x7fff) AM_NOP
+ AM_RANGE(0x8000, 0x83ff) AM_MIRROR(0x3c00) AM_RAM
+ AM_RANGE(0xc000, 0xc000) AM_MIRROR(0x3fff) AM_READ(soundlatch2_r) AM_WRITENOP
+ADDRESS_MAP_END
+
+
+
+/*************************************
+ *
+ * Red Alert audio start
+ *
+ *************************************/
+
+static SOUND_START( redalert )
+{
+ SOUND_START_CALL(redalert_audio);
+}
+
+
+
+/*************************************
+ *
+ * Red Alert audio board (m37b)
+ *
+ *************************************/
+
+static MACHINE_CONFIG_FRAGMENT( redalert_audio_m37b )
+
+ MCFG_CPU_ADD("audiocpu", M6502, REDALERT_AUDIO_CPU_CLOCK)
+ MCFG_CPU_PROGRAM_MAP(redalert_audio_map)
+ MCFG_CPU_PERIODIC_INT(irq0_line_hold, REDALERT_AUDIO_CPU_IRQ_FREQ)
+
+ MCFG_SOUND_ADD("aysnd", AY8910, REDALERT_AY8910_CLOCK)
+ MCFG_SOUND_CONFIG(redalert_ay8910_interface)
+ MCFG_SOUND_ROUTE(0, "mono", 0.50)
+ MCFG_SOUND_ROUTE(1, "mono", 0.50)
+ /* channel C is used a noise source and is not connected to a speaker */
+
+MACHINE_CONFIG_END
+
+/*************************************
+ *
+ * Red Alert voice board (ue17b)
+ *
+ *************************************/
+
+static MACHINE_CONFIG_FRAGMENT( redalert_audio_voice )
+
+ MCFG_CPU_ADD("voice", I8085A, REDALERT_VOICE_CPU_CLOCK)
+ MCFG_CPU_CONFIG(redalert_voice_i8085_config)
+ MCFG_CPU_PROGRAM_MAP(redalert_voice_map)
+
+ MCFG_SOUND_ADD("cvsd", HC55516, REDALERT_HC55516_CLOCK)
+ MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.50)
+MACHINE_CONFIG_END
+
+/*************************************
+ *
+ * Red Alert
+ *
+ *************************************/
+
+MACHINE_CONFIG_FRAGMENT( redalert_audio )
+
+ MCFG_SPEAKER_STANDARD_MONO("mono")
+
+ MCFG_FRAGMENT_ADD( redalert_audio_m37b )
+ MCFG_FRAGMENT_ADD( redalert_audio_voice )
+
+ MCFG_SOUND_START( redalert )
+
+MACHINE_CONFIG_END
+
+/*************************************
+ *
+ * Red Alert
+ *
+ *************************************/
+
+MACHINE_CONFIG_FRAGMENT( ww3_audio )
+
+ MCFG_SPEAKER_STANDARD_MONO("mono")
+
+ MCFG_FRAGMENT_ADD( redalert_audio_m37b )
+
+ MCFG_SOUND_START( redalert_audio )
+
+MACHINE_CONFIG_END
+
+/*************************************
+ *
+ * Demoneye-X audio board
+ *
+ *************************************/
+
+
+WRITE8_HANDLER( demoneye_audio_command_w )
+{
+ /* the byte is connected to port A of the AY8910 */
+ soundlatch_w(space, 0, data);
+ cputag_set_input_line(space->machine(), "audiocpu", INPUT_LINE_NMI, PULSE_LINE);
+}
+
+
+static WRITE8_DEVICE_HANDLER( demoneye_ay8910_latch_1_w )
+{
+ redalert_state *state = device->machine().driver_data<redalert_state>();
+ state->m_ay8910_latch_1 = data;
+}
+
+
+static READ8_DEVICE_HANDLER( demoneye_ay8910_latch_2_r )
+{
+ redalert_state *state = device->machine().driver_data<redalert_state>();
+ return state->m_ay8910_latch_2;
+}
+
+
+static WRITE8_DEVICE_HANDLER( demoneye_ay8910_data_w )
+{
+ redalert_state *state = device->machine().driver_data<redalert_state>();
+ device_t *ay1 = device->machine().device("ay1");
+ device_t *ay2 = device->machine().device("ay2");
+
+ switch (state->m_ay8910_latch_1 & 0x03)
+ {
+ case 0x00:
+ if (state->m_ay8910_latch_1 & 0x10)
+ ay8910_data_w(ay1, 0, data);
+
+ if (state->m_ay8910_latch_1 & 0x20)
+ ay8910_data_w(ay2, 0, data);
+
+ break;
+
+ case 0x01:
+ if (state->m_ay8910_latch_1 & 0x10)
+ state->m_ay8910_latch_2 = ay8910_r(ay1, 0);
+
+ if (state->m_ay8910_latch_1 & 0x20)
+ state->m_ay8910_latch_2 = ay8910_r(ay2, 0);
+
+ break;
+
+ case 0x03:
+ if (state->m_ay8910_latch_1 & 0x10)
+ ay8910_address_w(ay1, 0, data);
+
+ if (state->m_ay8910_latch_1 & 0x20)
+ ay8910_address_w(ay2, 0, data);
+
+ break;
+
+ default:
+ logerror("demoneye_ay8910_data_w called with latch %02X data %02X\n", state->m_ay8910_latch_1, data);
+ break;
+ }
+}
+
+
+static ADDRESS_MAP_START( demoneye_audio_map, AS_PROGRAM, 8 )
+ ADDRESS_MAP_GLOBAL_MASK(0x3fff)
+ AM_RANGE(0x0000, 0x007f) AM_RAM
+ AM_RANGE(0x0500, 0x0503) AM_DEVREADWRITE_MODERN("sndpia", pia6821_device, read, write)
+ AM_RANGE(0x2000, 0x3fff) AM_ROM
+ADDRESS_MAP_END
+
+
+static const ay8910_interface demoneye_ay8910_interface =
+{
+ AY8910_LEGACY_OUTPUT,
+ AY8910_DEFAULT_LOADS,
+ DEVCB_MEMORY_HANDLER("audiocpu", PROGRAM, soundlatch_r),
+ DEVCB_NULL, /* port A/B read */
+ DEVCB_NULL,
+ DEVCB_NULL /* port A/B write */
+};
+
+
+static const pia6821_interface demoneye_pia_intf =
+{
+ DEVCB_HANDLER(demoneye_ay8910_latch_2_r), /* port A in */
+ DEVCB_NULL, /* port B in */
+ DEVCB_NULL, /* line CA1 in */
+ DEVCB_NULL, /* line CB1 in */
+ DEVCB_NULL, /* line CA2 in */
+ DEVCB_NULL, /* line CB2 in */
+ DEVCB_HANDLER(demoneye_ay8910_data_w), /* port A out */
+ DEVCB_HANDLER(demoneye_ay8910_latch_1_w), /* port B out */
+ DEVCB_NULL, /* line CA2 out */
+ DEVCB_NULL, /* port CB2 out */
+ DEVCB_NULL, /* IRQA */
+ DEVCB_NULL /* IRQB */
+};
+
+
+
+/*************************************
+ *
+ * Demoneye-X audio start
+ *
+ *************************************/
+
+static SOUND_START( demoneye )
+{
+ redalert_state *state = machine.driver_data<redalert_state>();
+ state->save_item(NAME(state->m_ay8910_latch_1));
+ state->save_item(NAME(state->m_ay8910_latch_2));
+}
+
+
+
+/*************************************
+ *
+ * Demoneye-X machine driver
+ *
+ *************************************/
+
+MACHINE_CONFIG_FRAGMENT( demoneye_audio )
+
+ MCFG_CPU_ADD("audiocpu", M6802, DEMONEYE_AUDIO_CPU_CLOCK)
+ MCFG_CPU_PROGRAM_MAP(demoneye_audio_map)
+ MCFG_CPU_PERIODIC_INT(irq0_line_hold, REDALERT_AUDIO_CPU_IRQ_FREQ) /* guess */
+
+ MCFG_PIA6821_ADD("sndpia", demoneye_pia_intf)
+
+ MCFG_SOUND_START( demoneye )
+
+ MCFG_SPEAKER_STANDARD_MONO("mono")
+
+ MCFG_SOUND_ADD("ay1", AY8910, DEMONEYE_AY8910_CLOCK)
+ MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.50)
+
+ MCFG_SOUND_ADD("ay2", AY8910, DEMONEYE_AY8910_CLOCK)
+ MCFG_SOUND_CONFIG(demoneye_ay8910_interface)
+ MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.50)
+MACHINE_CONFIG_END