summaryrefslogtreecommitdiffstatshomepage
path: root/trunk/src/mame/audio/atarijsa.c
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/src/mame/audio/atarijsa.c')
-rw-r--r--trunk/src/mame/audio/atarijsa.c978
1 files changed, 978 insertions, 0 deletions
diff --git a/trunk/src/mame/audio/atarijsa.c b/trunk/src/mame/audio/atarijsa.c
new file mode 100644
index 00000000000..bfec28f33b3
--- /dev/null
+++ b/trunk/src/mame/audio/atarijsa.c
@@ -0,0 +1,978 @@
+/***************************************************************************
+
+Atari Audio Board II
+--------------------
+
+6502 MEMORY MAP
+
+Function Address R/W Data
+---------------------------------------------------------------
+Program RAM 0000-1FFF R/W D0-D7
+
+Music (YM-2151) 2000-2001 R/W D0-D7
+
+Read 68010 Port (Input Buffer) 280A R D0-D7
+
+Self-test 280C R D7
+Output Buffer Full (@2A02) (Active High) R D5
+Left Coin Switch R D1
+Right Coin Switch R D0
+
+Interrupt acknowledge 2A00 W xx
+Write 68010 Port (Outbut Buffer) 2A02 W D0-D7
+Banked ROM select (at 3000-3FFF) 2A04 W D6-D7
+??? 2A06 W
+
+Effects 2C00-2C0F R/W D0-D7
+
+Banked Program ROM (4 pages) 3000-3FFF R D0-D7
+Static Program ROM (48K bytes) 4000-FFFF R D0-D7
+
+TODO:
+JSA-i: stereo gating for POKEY/TMS5220C is currently only mono, only looking at ym2151_ct1;
+ the two commented-out lines would be correct if stereo volume-set functions were written.
+ALL: the LPF (low pass filter) bit which selectively places a lowpass filter in the output
+ path for all channels is currently unimplemented; someone who knows analog magic will need
+ to handle this.
+
+****************************************************************************/
+
+#include "emu.h"
+#include "cpu/m6502/m6502.h"
+#include "sound/tms5220.h"
+#include "sound/2151intf.h"
+#include "sound/okim6295.h"
+#include "sound/pokey.h"
+#include "machine/atarigen.h"
+#include "audio/atarijsa.h"
+
+
+#define JSA_MASTER_CLOCK XTAL_3_579545MHz
+
+
+static UINT8 *bank_base;
+static UINT8 *bank_source_data;
+
+static cpu_device *jsacpu;
+static const char *test_port;
+static UINT16 test_mask;
+
+static pokeyn_device *pokey;
+static ym2151_device *ym2151;
+static device_t *tms5220;
+static okim6295_device *oki6295;
+static okim6295_device *oki6295_l, *oki6295_r;
+
+static UINT8 overall_volume;
+static UINT8 pokey_volume;
+static UINT8 ym2151_volume;
+static UINT8 tms5220_volume;
+static UINT8 oki6295_volume;
+static UINT8 ym2151_ct1;
+static UINT8 ym2151_ct2;
+
+static void update_all_volumes(running_machine &machine);
+
+static READ8_HANDLER( jsa1_io_r );
+static WRITE8_HANDLER( jsa1_io_w );
+static READ8_HANDLER( jsa2_io_r );
+static WRITE8_HANDLER( jsa2_io_w );
+static READ8_HANDLER( jsa3_io_r );
+static WRITE8_HANDLER( jsa3_io_w );
+
+
+/*************************************
+ *
+ * OKI banked address map
+ *
+ *************************************/
+
+static ADDRESS_MAP_START( jsa3_oki_map, AS_0, 8, driver_device )
+ AM_RANGE(0x00000, 0x1ffff) AM_ROMBANK("bank12")
+ AM_RANGE(0x20000, 0x3ffff) AM_ROMBANK("bank13")
+ADDRESS_MAP_END
+
+static ADDRESS_MAP_START( jsa3_oki2_map, AS_0, 8, driver_device )
+ AM_RANGE(0x00000, 0x1ffff) AM_ROMBANK("bank14")
+ AM_RANGE(0x20000, 0x3ffff) AM_ROMBANK("bank15")
+ADDRESS_MAP_END
+
+
+
+/*************************************
+ *
+ * State save
+ *
+ *************************************/
+
+static void init_save_state(running_machine &machine)
+{
+ state_save_register_global(machine, overall_volume);
+ state_save_register_global(machine, pokey_volume);
+ state_save_register_global(machine, ym2151_volume);
+ state_save_register_global(machine, tms5220_volume);
+ state_save_register_global(machine, oki6295_volume);
+}
+
+
+
+/*************************************
+ *
+ * External interfaces
+ *
+ *************************************/
+
+void atarijsa_init(running_machine &machine, const char *testport, int testmask)
+{
+ UINT8 *rgn;
+
+ /* copy in the parameters */
+ jsacpu = machine.device<cpu_device>("jsa");
+ assert_always(jsacpu != NULL, "Could not find JSA CPU!");
+ test_port = testport;
+ test_mask = testmask;
+
+ /* predetermine the bank base */
+ rgn = machine.root_device().memregion("jsa")->base();
+ bank_base = &rgn[0x03000];
+ bank_source_data = &rgn[0x10000];
+
+ /* determine which sound hardware is installed */
+ tms5220 = machine.device("tms");
+ ym2151 = machine.device<ym2151_device>("ymsnd");
+ pokey = machine.device<pokeyn_device>("pokey");
+ oki6295 = machine.device<okim6295_device>("adpcm");
+ oki6295_l = machine.device<okim6295_device>("adpcml");
+ oki6295_r = machine.device<okim6295_device>("adpcmr");
+
+ /* install POKEY memory handlers */
+ if (pokey != NULL)
+ jsacpu->memory().space(AS_PROGRAM)->install_readwrite_handler(0x2c00, 0x2c0f, read8_delegate(FUNC(pokeyn_device::read),pokey), write8_delegate(FUNC(pokeyn_device::write),pokey));
+
+ init_save_state(machine);
+ atarijsa_reset();
+
+ /* initialize JSA III ADPCM */
+ {
+ static const char *const regions[] = { "adpcm", "adpcml", "adpcmr" };
+ int rgn;
+
+ /* expand the ADPCM data to avoid lots of memcpy's during gameplay */
+ /* the upper 128k is fixed, the lower 128k is bankswitched */
+ for (rgn = 0; rgn < ARRAY_LENGTH(regions); rgn++)
+ {
+ UINT8 *base = machine.root_device().memregion(regions[rgn])->base();
+ if (base != NULL && machine.root_device().memregion(regions[rgn])->bytes() >= 0x80000)
+ {
+ const char *bank = (rgn != 2) ? "bank12" : "bank14";
+ const char *bank_plus_1 = (rgn != 2) ? "bank13" : "bank15";
+ machine.root_device().membank(bank)->configure_entries(0, 2, base + 0x00000, 0x00000);
+ machine.root_device().membank(bank)->configure_entries(2, 2, base + 0x20000, 0x20000);
+ machine.root_device().membank(bank_plus_1)->set_base(base + 0x60000);
+ }
+ }
+ }
+}
+
+
+void atarijsa_reset(void)
+{
+ /* reset the sound I/O system */
+ atarigen_sound_io_reset(jsacpu);
+
+ /* reset the static states */
+ overall_volume = 100;
+ pokey_volume = 100;
+ ym2151_volume = 100;
+ tms5220_volume = 100;
+ oki6295_volume = 100;
+ ym2151_ct1 = 0;
+ ym2151_ct2 = 0;
+
+ /* Guardians of the Hood assumes we're reset to bank 0 on startup */
+ memcpy(bank_base, &bank_source_data[0x0000], 0x1000);
+}
+
+
+
+/*************************************
+ *
+ * JSA I I/O handlers
+ *
+ *************************************/
+
+static READ8_HANDLER( jsa1_io_r )
+{
+ atarigen_state *atarigen = space->machine().driver_data<atarigen_state>();
+ int result = 0xff;
+
+ switch (offset & 0x206)
+ {
+ case 0x000: /* n/c */
+ logerror("atarijsa: Unknown read at %04X\n", offset & 0x206);
+ break;
+
+ case 0x002: /* /RDP */
+ result = atarigen_6502_sound_r(space, offset);
+ break;
+
+ case 0x004: /* /RDIO */
+ /*
+ 0x80 = self test
+ 0x40 = NMI line state (active low)
+ 0x20 = sound output full
+ 0x10 = TMS5220 ready (active low)
+ 0x08 = +5V
+ 0x04 = +5V
+ 0x02 = coin 2
+ 0x01 = coin 1
+ */
+ result = space->machine().root_device().ioport("JSAI")->read();
+ if (!(space->machine().root_device().ioport(test_port)->read() & test_mask)) result ^= 0x80;
+ if (atarigen->m_cpu_to_sound_ready) result ^= 0x40;
+ if (atarigen->m_sound_to_cpu_ready) result ^= 0x20;
+ if ((tms5220 != NULL) && (tms5220_readyq_r(tms5220) == 0))
+ result |= 0x10;
+ else
+ result &= ~0x10;
+ break;
+
+ case 0x006: /* /IRQACK */
+ atarigen_6502_irq_ack_r(space, 0);
+ break;
+
+ case 0x200: /* /VOICE */
+ case 0x202: /* /WRP */
+ case 0x204: /* /WRIO */
+ case 0x206: /* /MIX */
+ logerror("atarijsa: Unknown read at %04X\n", offset & 0x206);
+ break;
+ }
+
+ return result;
+}
+
+
+static WRITE8_HANDLER( jsa1_io_w )
+{
+ switch (offset & 0x206)
+ {
+ case 0x000: /* n/c */
+ case 0x002: /* /RDP */
+ case 0x004: /* /RDIO */
+ logerror("atarijsa: Unknown write (%02X) at %04X\n", data & 0xff, offset & 0x206);
+ break;
+
+ case 0x006: /* /IRQACK */
+ atarigen_6502_irq_ack_r(space, 0);
+ break;
+
+ case 0x200: /* /VOICE */
+ if (tms5220 != NULL)
+ tms5220_data_w(tms5220, 0, data);
+ break;
+
+ case 0x202: /* /WRP */
+ atarigen_6502_sound_w(space, offset, data);
+ break;
+
+ case 0x204: /* WRIO */
+ /*
+ 0xc0 = bank address
+ 0x20 = coin counter 2
+ 0x10 = coin counter 1
+ 0x08 = squeak (tweaks the 5220 frequency)
+ 0x04 = TMS5220 reset (actually the read strobe) (active low)
+ 0x02 = TMS5220 write strobe (active low)
+ 0x01 = YM2151 reset (active low)
+ */
+
+ /* handle TMS5220 I/O */
+ if (tms5220 != NULL)
+ {
+ int count;
+ tms5220_wsq_w(tms5220, (data&0x02)>>1);
+ tms5220_rsq_w(tms5220, (data&0x04)>>2);
+ count = 5 | ((data >> 2) & 2);
+ tms5220_set_frequency(tms5220, JSA_MASTER_CLOCK*2 / (16 - count));
+ }
+
+ /* reset the YM2151 if needed */
+ if ((data&1) == 0) devtag_reset(space->machine(), "ymsnd");
+
+ /* coin counters */
+ coin_counter_w(space->machine(), 1, (data >> 5) & 1);
+ coin_counter_w(space->machine(), 0, (data >> 4) & 1);
+
+ /* update the bank */
+ memcpy(bank_base, &bank_source_data[0x1000 * ((data >> 6) & 3)], 0x1000);
+ break;
+
+ case 0x206: /* MIX */
+ /*
+ 0xc0 = TMS5220 volume (0-3)
+ 0x30 = POKEY volume (0-3)
+ 0x0e = YM2151 volume (0-7)
+ 0x01 = low-pass filter enable
+ */
+ tms5220_volume = ((data >> 6) & 3) * 100 / 3;
+ pokey_volume = ((data >> 4) & 3) * 100 / 3;
+ ym2151_volume = ((data >> 1) & 7) * 100 / 7;
+ update_all_volumes(space->machine());
+ break;
+ }
+}
+
+
+
+/*************************************
+ *
+ * JSA II I/O handlers
+ *
+ *************************************/
+
+static READ8_HANDLER( jsa2_io_r )
+{
+ atarigen_state *atarigen = space->machine().driver_data<atarigen_state>();
+ int result = 0xff;
+
+ switch (offset & 0x206)
+ {
+ case 0x000: /* /RDV */
+ if (oki6295 != NULL)
+ result = oki6295->read(*space, offset);
+ else
+ logerror("atarijsa: Unknown read at %04X\n", offset & 0x206);
+ break;
+
+ case 0x002: /* /RDP */
+ result = atarigen_6502_sound_r(space, offset);
+ break;
+
+ case 0x004: /* /RDIO */
+ /*
+ 0x80 = self test
+ 0x40 = NMI line state (active low)
+ 0x20 = sound output full
+ 0x10 = +5V
+ 0x08 = +5V
+ 0x04 = +5V
+ 0x02 = coin 2
+ 0x01 = coin 1
+ */
+ result = space->machine().root_device().ioport("JSAII")->read();
+ if (!(space->machine().root_device().ioport(test_port)->read() & test_mask)) result ^= 0x80;
+ if (atarigen->m_cpu_to_sound_ready) result ^= 0x40;
+ if (atarigen->m_sound_to_cpu_ready) result ^= 0x20;
+ break;
+
+ case 0x006: /* /IRQACK */
+ atarigen_6502_irq_ack_r(space, 0);
+ break;
+
+ case 0x200: /* /WRV */
+ case 0x202: /* /WRP */
+ case 0x204: /* /WRIO */
+ case 0x206: /* /MIX */
+ logerror("atarijsa: Unknown read at %04X\n", offset & 0x206);
+ break;
+ }
+
+ return result;
+}
+
+
+static WRITE8_HANDLER( jsa2_io_w )
+{
+ switch (offset & 0x206)
+ {
+ case 0x000: /* /RDV */
+ case 0x002: /* /RDP */
+ case 0x004: /* /RDIO */
+ logerror("atarijsa: Unknown write (%02X) at %04X\n", data & 0xff, offset & 0x206);
+ break;
+
+ case 0x006: /* /IRQACK */
+ atarigen_6502_irq_ack_r(space, 0);
+ break;
+
+ case 0x200: /* /WRV */
+ if (oki6295 != NULL)
+ oki6295->write(*space, offset, data);
+ else
+ logerror("atarijsa: Unknown write (%02X) at %04X\n", data & 0xff, offset & 0x206);
+ break;
+
+ case 0x202: /* /WRP */
+ atarigen_6502_sound_w(space, offset, data);
+ break;
+
+ case 0x204: /* /WRIO */
+ /*
+ 0xc0 = bank address
+ 0x20 = coin counter 2
+ 0x10 = coin counter 1
+ 0x08 = voice frequency (tweaks the OKI6295 frequency)
+ 0x04 = OKI6295 reset (active low)
+ 0x02 = n/c
+ 0x01 = YM2151 reset (active low)
+ */
+
+ /* reset the YM2151 if needed */
+ if ((data&1) == 0) devtag_reset(space->machine(), "ymsnd");
+
+ /* update the bank */
+ memcpy(bank_base, &bank_source_data[0x1000 * ((data >> 6) & 3)], 0x1000);
+
+ /* coin counters */
+ coin_counter_w(space->machine(), 1, (data >> 5) & 1);
+ coin_counter_w(space->machine(), 0, (data >> 4) & 1);
+
+ /* update the OKI frequency */
+ if (oki6295 != NULL)
+ oki6295->set_pin7(data & 8);
+ break;
+
+ case 0x206: /* /MIX */
+ /*
+ 0xc0 = n/c
+ 0x20 = low-pass filter enable
+ 0x10 = n/c
+ 0x0e = YM2151 volume (0-7)
+ 0x01 = OKI6295 volume (0-1)
+ */
+ ym2151_volume = ((data >> 1) & 7) * 100 / 7;
+ oki6295_volume = 50 + (data & 1) * 50;
+ update_all_volumes(space->machine());
+ break;
+ }
+}
+
+
+
+/*************************************
+ *
+ * JSA III I/O handlers
+ *
+ *************************************/
+
+static READ8_HANDLER( jsa3_io_r )
+{
+ atarigen_state *atarigen = space->machine().driver_data<atarigen_state>();
+ int result = 0xff;
+
+ switch (offset & 0x206)
+ {
+ case 0x000: /* /RDV */
+ if (oki6295 != NULL)
+ result = oki6295->read(*space, offset);
+ break;
+
+ case 0x002: /* /RDP */
+ result = atarigen_6502_sound_r(space, offset);
+ break;
+
+ case 0x004: /* /RDIO */
+ /*
+ 0x80 = self test (active high)
+ 0x40 = NMI line state (active high)
+ 0x20 = sound output full (active high)
+ 0x10 = self test (active high)
+ 0x08 = service (active high)
+ 0x04 = tilt (active high)
+ 0x02 = coin L (active high)
+ 0x01 = coin R (active high)
+ */
+ result = space->machine().root_device().ioport("JSAIII")->read();
+ if (!(space->machine().root_device().ioport(test_port)->read() & test_mask)) result ^= 0x90;
+ if (atarigen->m_cpu_to_sound_ready) result ^= 0x40;
+ if (atarigen->m_sound_to_cpu_ready) result ^= 0x20;
+ break;
+
+ case 0x006: /* /IRQACK */
+ atarigen_6502_irq_ack_r(space, 0);
+ break;
+
+ case 0x200: /* /WRV */
+ case 0x202: /* /WRP */
+ case 0x204: /* /WRIO */
+ case 0x206: /* /MIX */
+ logerror("atarijsa: Unknown read at %04X\n", offset & 0x206);
+ break;
+ }
+
+ return result;
+}
+
+
+static WRITE8_HANDLER( jsa3_io_w )
+{
+ switch (offset & 0x206)
+ {
+ case 0x000: /* /RDV */
+ overall_volume = data * 100 / 127;
+ update_all_volumes(space->machine());
+ break;
+
+ case 0x002: /* /RDP */
+ case 0x004: /* /RDIO */
+ logerror("atarijsa: Unknown write (%02X) at %04X\n", data & 0xff, offset & 0x206);
+ break;
+
+ case 0x006: /* /IRQACK */
+ atarigen_6502_irq_ack_r(space, 0);
+ break;
+
+ case 0x200: /* /WRV */
+ if (oki6295 != NULL)
+ oki6295->write(*space, offset, data);
+ break;
+
+ case 0x202: /* /WRP */
+ atarigen_6502_sound_w(space, offset, data);
+ break;
+
+ case 0x204: /* /WRIO */
+ /*
+ 0xc0 = bank address
+ 0x20 = coin counter 2
+ 0x10 = coin counter 1
+ 0x08 = voice frequency (tweaks the OKI6295 frequency)
+ 0x04 = OKI6295 reset (active low)
+ 0x02 = OKI6295 bank bit 0
+ 0x01 = YM2151 reset (active low)
+ */
+
+ /* reset the YM2151 if needed */
+ if ((data&1) == 0) devtag_reset(space->machine(), "ymsnd");
+
+ /* update the OKI bank */
+ if (oki6295 != NULL)
+ space->machine().root_device().membank("bank12")->set_entry((space->machine().root_device().membank("bank12")->entry() & 2) | ((data >> 1) & 1));
+
+ /* update the bank */
+ memcpy(bank_base, &bank_source_data[0x1000 * ((data >> 6) & 3)], 0x1000);
+
+ /* coin counters */
+ coin_counter_w(space->machine(), 1, (data >> 5) & 1);
+ coin_counter_w(space->machine(), 0, (data >> 4) & 1);
+
+ /* update the OKI frequency */
+ if (oki6295 != NULL) oki6295->set_pin7(data & 8);
+ break;
+
+ case 0x206: /* /MIX */
+ /*
+ 0xc0 = n/c
+ 0x20 = low-pass filter enable
+ 0x10 = OKI6295 bank bit 1
+ 0x0e = YM2151 volume (0-7)
+ 0x01 = OKI6295 volume (0-1)
+ */
+
+ /* update the OKI bank */
+ if (oki6295 != NULL)
+ space->machine().root_device().membank("bank12")->set_entry((space->machine().root_device().membank("bank12")->entry() & 1) | ((data >> 3) & 2));
+
+ /* update the volumes */
+ ym2151_volume = ((data >> 1) & 7) * 100 / 7;
+ oki6295_volume = 50 + (data & 1) * 50;
+ update_all_volumes(space->machine());
+ break;
+ }
+}
+
+
+
+/*************************************
+ *
+ * JSA IIIS I/O handlers
+ *
+ *************************************/
+
+static READ8_HANDLER( jsa3s_io_r )
+{
+ atarigen_state *atarigen = space->machine().driver_data<atarigen_state>();
+ int result = 0xff;
+
+ switch (offset & 0x206)
+ {
+ case 0x000: /* /RDV */
+ if (oki6295_l != NULL)
+ result = ((offset & 1) ? oki6295_r : oki6295_l)->read(*space, offset);
+ break;
+
+ case 0x002: /* /RDP */
+ result = atarigen_6502_sound_r(space, offset);
+ break;
+
+ case 0x004: /* /RDIO */
+ /*
+ 0x80 = self test (active high)
+ 0x40 = NMI line state (active high)
+ 0x20 = sound output full (active high)
+ 0x10 = self test (active high)
+ 0x08 = service (active high)
+ 0x04 = tilt (active high)
+ 0x02 = coin L (active high)
+ 0x01 = coin R (active high)
+ */
+ result = space->machine().root_device().ioport("JSAIII")->read();
+ if (!(space->machine().root_device().ioport(test_port)->read() & test_mask)) result ^= 0x90;
+ if (atarigen->m_cpu_to_sound_ready) result ^= 0x40;
+ if (atarigen->m_sound_to_cpu_ready) result ^= 0x20;
+ break;
+
+ case 0x006: /* /IRQACK */
+ atarigen_6502_irq_ack_r(space, 0);
+ break;
+
+ case 0x200: /* /WRV */
+ case 0x202: /* /WRP */
+ case 0x204: /* /WRIO */
+ case 0x206: /* /MIX */
+ logerror("atarijsa: Unknown read at %04X\n", offset & 0x206);
+ break;
+ }
+
+ return result;
+}
+
+
+static WRITE8_HANDLER( jsa3s_io_w )
+{
+ switch (offset & 0x206)
+ {
+ case 0x000: /* /RDV */
+ overall_volume = data * 100 / 127;
+ update_all_volumes(space->machine());
+ break;
+
+ case 0x002: /* /RDP */
+ case 0x004: /* /RDIO */
+ logerror("atarijsa: Unknown write (%02X) at %04X\n", data & 0xff, offset & 0x206);
+ break;
+
+ case 0x006: /* /IRQACK */
+ atarigen_6502_irq_ack_r(space, 0);
+ break;
+
+ case 0x200: /* /WRV */
+ if (oki6295_l != NULL)
+ ((offset & 1) ? oki6295_r : oki6295_l)->write(*space, 0, data);
+ break;
+
+ case 0x202: /* /WRP */
+ atarigen_6502_sound_w(space, offset, data);
+ break;
+
+ case 0x204: /* /WRIO */
+ /*
+ 0xc0 = bank address
+ 0x20 = coin counter 2
+ 0x10 = coin counter 1
+ 0x08 = voice frequency (tweaks the OKI6295 frequency)
+ 0x04 = OKI6295 reset (active low)
+ 0x02 = left OKI6295 bank bit 0
+ 0x01 = YM2151 reset (active low)
+ */
+
+ /* reset the YM2151 if needed */
+ if ((data&1) == 0) devtag_reset(space->machine(), "ymsnd");
+
+ /* update the OKI bank */
+ space->machine().root_device().membank("bank12")->set_entry((space->machine().root_device().membank("bank12")->entry() & 2) | ((data >> 1) & 1));
+
+ /* update the bank */
+ memcpy(bank_base, &bank_source_data[0x1000 * ((data >> 6) & 3)], 0x1000);
+
+ /* coin counters */
+ coin_counter_w(space->machine(), 1, (data >> 5) & 1);
+ coin_counter_w(space->machine(), 0, (data >> 4) & 1);
+
+ /* update the OKI frequency */
+ oki6295_l->set_pin7(data & 8);
+ oki6295_r->set_pin7(data & 8);
+ break;
+
+ case 0x206: /* /MIX */
+ /*
+ 0xc0 = right OKI6295 bank bits 0-1
+ 0x20 = low-pass filter enable
+ 0x10 = left OKI6295 bank bit 1
+ 0x0e = YM2151 volume (0-7)
+ 0x01 = OKI6295 volume (0-1)
+ */
+
+ /* update the OKI bank */
+ space->machine().root_device().membank("bank12")->set_entry((space->machine().root_device().membank("bank12")->entry() & 1) | ((data >> 3) & 2));
+ space->machine().root_device().membank("bank14")->set_entry(data >> 6);
+
+ /* update the volumes */
+ ym2151_volume = ((data >> 1) & 7) * 100 / 7;
+ oki6295_volume = 50 + (data & 1) * 50;
+ update_all_volumes(space->machine());
+ break;
+ }
+}
+
+static WRITE8_DEVICE_HANDLER( ym2151_ctl_w )
+{
+ ym2151_ct1 = data&0x1;
+ ym2151_ct2 = (data&0x2)>>1;
+ update_all_volumes(device->machine());
+}
+
+
+/*************************************
+ *
+ * Volume helpers
+ *
+ *************************************/
+
+static void update_all_volumes(running_machine &machine )
+{
+ if (pokey != NULL) atarigen_set_pokey_vol(machine, (overall_volume * pokey_volume / 100) * ym2151_ct1);
+ //if (pokey != NULL) atarigen_set_pokey_stereo_vol(machine, (overall_volume * pokey_volume / 100) * ym2151_ct1, (overall_volume * pokey_volume / 100) * ym2151_ct2);
+ if (ym2151 != NULL) atarigen_set_ym2151_vol(machine, overall_volume * ym2151_volume / 100);
+ if (tms5220 != NULL) atarigen_set_tms5220_vol(machine, (overall_volume * tms5220_volume / 100) * ym2151_ct1);
+ //if (tms5220 != NULL) atarigen_set_tms5220_stereo_vol(machine, (overall_volume * tms5220_volume / 100) * ym2151_ct1, (overall_volume * tms5220_volume / 100) * ym2151_ct2);
+ if (oki6295 != NULL || oki6295_l != NULL || oki6295_r != NULL) atarigen_set_oki6295_vol(machine, overall_volume * oki6295_volume / 100);
+}
+
+
+
+/*************************************
+ *
+ * Sound CPU memory handlers
+ *
+ *************************************/
+
+static ADDRESS_MAP_START( atarijsa1_map, AS_PROGRAM, 8, driver_device )
+ AM_RANGE(0x0000, 0x1fff) AM_RAM
+ AM_RANGE(0x2000, 0x2001) AM_DEVREADWRITE_LEGACY("ymsnd", ym2151_r, ym2151_w)
+ AM_RANGE(0x2800, 0x2bff) AM_READWRITE_LEGACY(jsa1_io_r, jsa1_io_w)
+ AM_RANGE(0x3000, 0xffff) AM_ROM
+ADDRESS_MAP_END
+
+
+static ADDRESS_MAP_START( atarijsa2_map, AS_PROGRAM, 8, driver_device )
+ AM_RANGE(0x0000, 0x1fff) AM_RAM
+ AM_RANGE(0x2000, 0x2001) AM_DEVREADWRITE_LEGACY("ymsnd", ym2151_r, ym2151_w)
+ AM_RANGE(0x2800, 0x2bff) AM_READWRITE_LEGACY(jsa2_io_r, jsa2_io_w)
+ AM_RANGE(0x3000, 0xffff) AM_ROM
+ADDRESS_MAP_END
+
+
+/* full map verified from schematics and Batman GALs */
+static ADDRESS_MAP_START( atarijsa3_map, AS_PROGRAM, 8, driver_device )
+ AM_RANGE(0x0000, 0x1fff) AM_RAM
+ AM_RANGE(0x2000, 0x2001) AM_MIRROR(0x07fe) AM_DEVREADWRITE_LEGACY("ymsnd", ym2151_r, ym2151_w)
+ AM_RANGE(0x2800, 0x2fff) AM_READWRITE_LEGACY(jsa3_io_r, jsa3_io_w)
+ AM_RANGE(0x3000, 0xffff) AM_ROM
+ADDRESS_MAP_END
+
+
+static ADDRESS_MAP_START( atarijsa3s_map, AS_PROGRAM, 8, driver_device )
+ AM_RANGE(0x0000, 0x1fff) AM_RAM
+ AM_RANGE(0x2000, 0x2001) AM_MIRROR(0x07fe) AM_DEVREADWRITE_LEGACY("ymsnd", ym2151_r, ym2151_w)
+ AM_RANGE(0x2800, 0x2fff) AM_READWRITE_LEGACY(jsa3s_io_r, jsa3s_io_w)
+ AM_RANGE(0x3000, 0xffff) AM_ROM
+ADDRESS_MAP_END
+
+
+
+/*************************************
+ *
+ * Sound definitions
+ *
+ *************************************/
+
+static const ym2151_interface ym2151_config =
+{
+ DEVCB_LINE(atarigen_ym2151_irq_gen),
+ DEVCB_HANDLER(ym2151_ctl_w)
+};
+
+
+
+/*************************************
+ *
+ * Machine drivers
+ *
+ *************************************/
+
+/* Used by Blasteroids */
+MACHINE_CONFIG_FRAGMENT( jsa_i_stereo )
+
+ /* basic machine hardware */
+ MCFG_CPU_ADD("jsa", M6502, JSA_MASTER_CLOCK/2)
+ MCFG_CPU_PROGRAM_MAP(atarijsa1_map)
+ MCFG_CPU_PERIODIC_INT(atarigen_6502_irq_gen, (double)JSA_MASTER_CLOCK/4/16/16/14)
+
+ /* sound hardware */
+ MCFG_SPEAKER_STANDARD_STEREO("lspeaker", "rspeaker")
+
+ MCFG_SOUND_ADD("ymsnd", YM2151, JSA_MASTER_CLOCK)
+ MCFG_SOUND_CONFIG(ym2151_config)
+ MCFG_SOUND_ROUTE(0, "lspeaker", 0.60)
+ MCFG_SOUND_ROUTE(1, "rspeaker", 0.60)
+MACHINE_CONFIG_END
+
+
+/* Used by Xybots */
+MACHINE_CONFIG_DERIVED( jsa_i_stereo_swapped, jsa_i_stereo )
+
+ /* basic machine hardware */
+
+ /* sound hardware */
+ MCFG_SOUND_REPLACE("ymsnd", YM2151, JSA_MASTER_CLOCK)
+ MCFG_SOUND_CONFIG(ym2151_config)
+ MCFG_SOUND_ROUTE(0, "rspeaker", 0.60)
+ MCFG_SOUND_ROUTE(1, "lspeaker", 0.60)
+MACHINE_CONFIG_END
+
+
+/* Used by Toobin', Vindicators */
+MACHINE_CONFIG_DERIVED( jsa_i_stereo_pokey, jsa_i_stereo )
+
+ /* basic machine hardware */
+
+ /* sound hardware */
+ MCFG_SOUND_ADD("pokey", POKEYN, JSA_MASTER_CLOCK/2)
+ MCFG_SOUND_ROUTE(ALL_OUTPUTS, "lspeaker", 0.40)
+ MCFG_SOUND_ROUTE(ALL_OUTPUTS, "rspeaker", 0.40)
+MACHINE_CONFIG_END
+
+
+/* Used by Escape from the Planet of the Robot Monsters */
+MACHINE_CONFIG_FRAGMENT( jsa_i_mono_speech )
+
+ /* basic machine hardware */
+ MCFG_CPU_ADD("jsa", M6502, JSA_MASTER_CLOCK/2)
+ MCFG_CPU_PROGRAM_MAP(atarijsa1_map)
+ MCFG_CPU_PERIODIC_INT(atarigen_6502_irq_gen, (double)JSA_MASTER_CLOCK/4/16/16/14)
+
+ /* sound hardware */
+ MCFG_SPEAKER_STANDARD_MONO("mono")
+
+ MCFG_SOUND_ADD("ymsnd", YM2151, JSA_MASTER_CLOCK)
+ MCFG_SOUND_CONFIG(ym2151_config)
+ MCFG_SOUND_ROUTE(0, "mono", 0.60)
+ MCFG_SOUND_ROUTE(1, "mono", 0.60)
+
+ MCFG_SOUND_ADD("tms", TMS5220C, JSA_MASTER_CLOCK*2/11) /* potentially JSA_MASTER_CLOCK/9 as well */
+ MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 1.0)
+MACHINE_CONFIG_END
+
+
+/* Used by Cyberball 2072, STUN Runner, Skull & Crossbones, ThunderJaws, Hydra, Pit Fighter */
+MACHINE_CONFIG_FRAGMENT( jsa_ii_mono )
+
+ /* basic machine hardware */
+ MCFG_CPU_ADD("jsa", M6502, JSA_MASTER_CLOCK/2)
+ MCFG_CPU_PROGRAM_MAP(atarijsa2_map)
+ MCFG_CPU_PERIODIC_INT(atarigen_6502_irq_gen, (double)JSA_MASTER_CLOCK/4/16/16/14)
+
+ /* sound hardware */
+ MCFG_SPEAKER_STANDARD_MONO("mono")
+
+ MCFG_SOUND_ADD("ymsnd", YM2151, JSA_MASTER_CLOCK)
+ MCFG_SOUND_CONFIG(ym2151_config)
+ MCFG_SOUND_ROUTE(0, "mono", 0.60)
+ MCFG_SOUND_ROUTE(1, "mono", 0.60)
+
+ MCFG_OKIM6295_ADD("adpcm", JSA_MASTER_CLOCK/3, OKIM6295_PIN7_HIGH)
+ MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.75)
+MACHINE_CONFIG_END
+
+
+/* Used by Batman, Guardians of the 'Hood, Road Riot 4WD, Steel Talons */
+MACHINE_CONFIG_DERIVED( jsa_iii_mono, jsa_ii_mono )
+
+ /* basic machine hardware */
+ MCFG_CPU_MODIFY("jsa")
+ MCFG_CPU_PROGRAM_MAP(atarijsa3_map)
+
+ MCFG_DEVICE_MODIFY("adpcm")
+ MCFG_DEVICE_ADDRESS_MAP(AS_0, jsa3_oki_map)
+MACHINE_CONFIG_END
+
+
+/* Used by Off the Wall */
+MACHINE_CONFIG_DERIVED( jsa_iii_mono_noadpcm, jsa_iii_mono )
+
+ /* basic machine hardware */
+
+ /* sound hardware */
+ MCFG_DEVICE_REMOVE("adpcm")
+MACHINE_CONFIG_END
+
+
+/* Used by Space Lords, Moto Frenzy, Road Riot's Revenge Rally */
+MACHINE_CONFIG_FRAGMENT( jsa_iiis_stereo )
+
+ /* basic machine hardware */
+ MCFG_CPU_ADD("jsa", M6502, JSA_MASTER_CLOCK/2)
+ MCFG_CPU_PROGRAM_MAP(atarijsa3s_map)
+ MCFG_CPU_PERIODIC_INT(atarigen_6502_irq_gen, (double)JSA_MASTER_CLOCK/4/16/16/14)
+
+ /* sound hardware */
+ MCFG_SPEAKER_STANDARD_STEREO("lspeaker", "rspeaker")
+
+ MCFG_SOUND_ADD("ymsnd", YM2151, JSA_MASTER_CLOCK)
+ MCFG_SOUND_CONFIG(ym2151_config)
+ MCFG_SOUND_ROUTE(0, "lspeaker", 0.60)
+ MCFG_SOUND_ROUTE(1, "rspeaker", 0.60)
+
+ MCFG_OKIM6295_ADD("adpcml", JSA_MASTER_CLOCK/3, OKIM6295_PIN7_HIGH)
+ MCFG_SOUND_ROUTE(ALL_OUTPUTS, "lspeaker", 0.75)
+ MCFG_DEVICE_ADDRESS_MAP(AS_0, jsa3_oki_map)
+
+ MCFG_OKIM6295_ADD("adpcmr", JSA_MASTER_CLOCK/3, OKIM6295_PIN7_HIGH)
+ MCFG_SOUND_ROUTE(ALL_OUTPUTS, "rspeaker", 0.75)
+ MCFG_DEVICE_ADDRESS_MAP(AS_0, jsa3_oki2_map)
+MACHINE_CONFIG_END
+
+
+/*************************************
+ *
+ * Port definitions
+ *
+ *************************************/
+
+INPUT_PORTS_START( atarijsa_i )
+ PORT_START("JSAI")
+ PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_COIN1 )
+ PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_COIN2 )
+ PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_COIN3 )
+ PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )
+ PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_UNUSED ) /* speech chip ready */
+ PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_UNUSED ) /* output buffer full */
+ PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED ) /* input buffer full */
+ PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED ) /* self test */
+INPUT_PORTS_END
+
+INPUT_PORTS_START( atarijsa_ii )
+ PORT_START("JSAII")
+ PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_COIN1 )
+ PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_COIN2 )
+ PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_COIN3 )
+ PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )
+ PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_UNUSED )
+ PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_UNUSED ) /* output buffer full */
+ PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED ) /* input buffer full */
+ PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED ) /* self test */
+INPUT_PORTS_END
+
+INPUT_PORTS_START( atarijsa_iii )
+ PORT_START("JSAIII")
+ PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_COIN2 )
+ PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_COIN1 )
+ PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_TILT )
+ PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_SERVICE )
+ PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_UNUSED ) /* self test */
+ PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_UNUSED ) /* output buffer full */
+ PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_UNUSED ) /* input buffer full */
+ PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED ) /* self test */
+INPUT_PORTS_END