// license:BSD-3-Clause
// copyright-holders:Aaron Giles
/***************************************************************************
atarijsa.c
Functions to emulate the Atari "JSA" audio boards
****************************************************************************
JSA I (stereo): used by:
* Blasteroids (YM2151 only)
* Toobin' (YM2151 + POKEY)
* Vindicators (YM2151 + POKEY)
* Escape from the Planets of the Robot Monsters (YM2151 + TMS5220)
JSA II (mono), used by:
* Cyberball 2072
* STUN Runner
* Skull & Crossbones
* ThunderJaws
* Hydra
* Pit Fighter
JSA III (mono), used by:
* Off the Wall (YM2151 only)
* Batman
* Guardians of the 'Hood
* Road Riot 4WD
* Steel Talons
JSA IIIs (stereo), used by:
* Space Lords
* Moto Frenzy
* Road Riot's Revenge Rally
****************************************************************************
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 "audio/atarijsa.h"
#define JSA_MASTER_CLOCK XTAL(3'579'545)
//**************************************************************************
// GLOBAL VARIABLES
//**************************************************************************
DEFINE_DEVICE_TYPE(ATARI_JSA_I, atari_jsa_i_device, "atjsa1", "Atari JSA I Sound Board")
DEFINE_DEVICE_TYPE(ATARI_JSA_II, atari_jsa_ii_device, "atjsa2", "Atari JSA II Sound Board")
DEFINE_DEVICE_TYPE(ATARI_JSA_III, atari_jsa_iii_device, "atjsa3", "Atari JSA III Sound Board")
DEFINE_DEVICE_TYPE(ATARI_JSA_IIIS, atari_jsa_iiis_device, "atjsa3s", "Atari JSA IIIs Sound Board")
//**************************************************************************
// MEMORY MAPS
//**************************************************************************
void atari_jsa_i_device::atarijsa1_map(address_map &map)
{
map(0x0000, 0x1fff).ram();
map(0x2000, 0x2001).rw("ym2151", FUNC(ym2151_device::read), FUNC(ym2151_device::write));
map(0x2800, 0x2800).mirror(0x01f9); // N/C
map(0x2802, 0x2802).mirror(0x01f9).r("soundcomm", FUNC(atari_sound_comm_device::sound_command_r)); // /RDP
map(0x2804, 0x2804).mirror(0x01f9).r(this, FUNC(atari_jsa_i_device::rdio_r)); // /RDIO
map(0x2806, 0x2806).mirror(0x01f9).rw("soundcomm", FUNC(atari_sound_comm_device::sound_irq_ack_r), FUNC(atari_sound_comm_device::sound_irq_ack_w)); // R/W=/IRQACK
map(0x2a00, 0x2a00).mirror(0x01f9).w(this, FUNC(atari_jsa_i_device::tms5220_voice)); // /VOICE
map(0x2a02, 0x2a02).mirror(0x01f9).w("soundcomm", FUNC(atari_sound_comm_device::sound_response_w)); // /WRP
map(0x2a04, 0x2a04).mirror(0x01f9).w(this, FUNC(atari_jsa_i_device::wrio_w)); // /WRIO
map(0x2a06, 0x2a06).mirror(0x01f9).w(this, FUNC(atari_jsa_i_device::mix_w)); // /MIX
map(0x2c00, 0x2c0f).mirror(0x03f0).rw(this, FUNC(atari_jsa_i_device::pokey_r), FUNC(atari_jsa_i_device::pokey_w));
map(0x3000, 0x3fff).bankr("cpubank");
map(0x4000, 0xffff).rom();
}
void atari_jsa_ii_device::atarijsa2_map(address_map &map)
{
map(0x0000, 0x1fff).ram();
map(0x2000, 0x2001).rw("ym2151", FUNC(ym2151_device::read), FUNC(ym2151_device::write));
map(0x2800, 0x2800).mirror(0x01f9).r(this, FUNC(atari_jsa_ii_device::oki_r)); // /RDV
map(0x2802, 0x2802).mirror(0x01f9).r("soundcomm", FUNC(atari_sound_comm_device::sound_command_r)); // /RDP
map(0x2804, 0x2804).mirror(0x01f9).r(this, FUNC(atari_jsa_ii_device::rdio_r)); // /RDIO
map(0x2806, 0x2806).mirror(0x01f9).rw("soundcomm", FUNC(atari_sound_comm_device::sound_irq_ack_r), FUNC(atari_sound_comm_device::sound_irq_ack_w)); // R/W=/IRQACK
map(0x2a00, 0x2a00).mirror(0x01f9).w(this, FUNC(atari_jsa_ii_device::oki_w)); // /WRV
map(0x2a02, 0x2a02).mirror(0x01f9).w("soundcomm", FUNC(atari_sound_comm_device::sound_response_w)); // /WRP
map(0x2a04, 0x2a04).mirror(0x01f9).w(this, FUNC(atari_jsa_ii_device::wrio_w)); // /WRIO
map(0x2a06, 0x2a06).mirror(0x01f9).w(this, FUNC(atari_jsa_ii_device::mix_w)); // /MIX
map(0x3000, 0x3fff).bankr("cpubank");
map(0x4000, 0xffff).rom();
}
// full map verified from schematics and Batman GALs
void atari_jsa_iii_device::atarijsa3_map(address_map &map)
{
map(0x0000, 0x1fff).ram();
map(0x2000, 0x2001).mirror(0x07fe).rw("ym2151", FUNC(ym2151_device::read), FUNC(ym2151_device::write));
map(0x2800, 0x2801).mirror(0x05f8).rw(this, FUNC(atari_jsa_iii_device::oki_r), FUNC(atari_jsa_iii_device::overall_volume_w)); // /RDV
map(0x2802, 0x2802).mirror(0x05f9).r("soundcomm", FUNC(atari_sound_comm_device::sound_command_r)); // /RDP
map(0x2804, 0x2804).mirror(0x05f9).r(this, FUNC(atari_jsa_iii_device::rdio_r)); // /RDIO
map(0x2806, 0x2806).mirror(0x05f9).rw("soundcomm", FUNC(atari_sound_comm_device::sound_irq_ack_r), FUNC(atari_sound_comm_device::sound_irq_ack_w)); // R/W=/IRQACK
map(0x2a00, 0x2a01).mirror(0x05f8).w(this, FUNC(atari_jsa_iii_device::oki_w)); // /WRV
map(0x2a02, 0x2a02).mirror(0x05f9).w("soundcomm", FUNC(atari_sound_comm_device::sound_response_w)); // /WRP
map(0x2a04, 0x2a04).mirror(0x05f9).w(this, FUNC(atari_jsa_iii_device::wrio_w)); // /WRIO
map(0x2a06, 0x2a06).mirror(0x05f9).w(this, FUNC(atari_jsa_iii_device::mix_w)); // /MIX
map(0x3000, 0x3fff).bankr("cpubank");
map(0x4000, 0xffff).rom();
}
void atari_jsa_iii_device::jsa3_oki1_map(address_map &map)
{
map(0x00000, 0x1ffff).bankr("oki1lo");
map(0x20000, 0x3ffff).bankr("oki1hi");
}
void atari_jsa_iiis_device::jsa3_oki2_map(address_map &map)
{
map(0x00000, 0x1ffff).bankr("oki2lo");
map(0x20000, 0x3ffff).bankr("oki2hi");
}
//**************************************************************************
// I/O PORT DEFINITIONS
//**************************************************************************
INPUT_PORTS_START( jsa_i_ioports )
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_CUSTOM ) PORT_READ_LINE_DEVICE_MEMBER("soundcomm", atari_sound_comm_device, sound_to_main_ready) // output buffer full
PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_CUSTOM ) PORT_READ_LINE_DEVICE_MEMBER("soundcomm", atari_sound_comm_device, main_to_sound_ready) // input buffer full
PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_DEVICE_MEMBER(DEVICE_SELF, atari_jsa_base_device, main_test_read_line) // self test
INPUT_PORTS_END
INPUT_PORTS_START( jsa_ii_ioports )
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_CUSTOM ) PORT_READ_LINE_DEVICE_MEMBER("soundcomm", atari_sound_comm_device, sound_to_main_ready) // output buffer full
PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_CUSTOM ) PORT_READ_LINE_DEVICE_MEMBER("soundcomm", atari_sound_comm_device, main_to_sound_ready) // input buffer full
PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_DEVICE_MEMBER(DEVICE_SELF, atari_jsa_base_device, main_test_read_line) // self test
INPUT_PORTS_END
INPUT_PORTS_START( jsa_iii_ioports )
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_CUSTOM ) PORT_READ_LINE_DEVICE_MEMBER(DEVICE_SELF, atari_jsa_base_device, main_test_read_line) // self test
PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_DEVICE_MEMBER("soundcomm", atari_sound_comm_device, sound_to_main_ready) // output buffer full
PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_CUSTOM ) PORT_READ_LINE_DEVICE_MEMBER("soundcomm", atari_sound_comm_device, main_to_sound_ready) // input buffer full
PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_DEVICE_MEMBER(DEVICE_SELF, atari_jsa_base_device, main_test_read_line) // self test
INPUT_PORTS_END
//**************************************************************************
// BASE DEVICE CLASS
//**************************************************************************
//-------------------------------------------------
// atari_jsa_base_device - constructor
//-------------------------------------------------
atari_jsa_base_device::atari_jsa_base_device(const machine_config &mconfig, device_type devtype, const char *tag, device_t *owner, uint32_t clock, int channels)
: device_t(mconfig, devtype, tag, owner, clock),
device_mixer_interface(mconfig, *this, channels),
m_soundcomm(*this, "soundcomm"),
m_jsacpu(*this, "cpu"),
m_ym2151(*this, "ym2151"),
m_cpu_region(*this, "cpu"),
m_cpu_bank(*this, "cpubank"),
m_test_read_cb(*this),
m_main_int_cb(*this),
m_ym2151_volume(1.0),
m_ym2151_ct1(0),
m_ym2151_ct2(0)
{
}
//-------------------------------------------------
// device_start: Start up the device
//-------------------------------------------------
void atari_jsa_base_device::device_start()
{
// configure CPU bank
m_cpu_bank->configure_entries(0, 4, m_cpu_region->base(), 0x1000);
// resolve devices
m_test_read_cb.resolve_safe(0);
m_main_int_cb.resolve_safe();
// save states
save_item(NAME(m_ym2151_volume));
save_item(NAME(m_ym2151_ct1));
save_item(NAME(m_ym2151_ct2));
}
//-------------------------------------------------
// device_reset: Reset the device
//-------------------------------------------------
void atari_jsa_base_device::device_reset()
{
// reset the static states
m_ym2151_volume = 1.0;
m_ym2151_ct1 = 0;
m_ym2151_ct2 = 0;
// Guardians of the Hood assumes we're reset to bank 0 on startup
m_cpu_bank->set_entry(0);
}
//-------------------------------------------------
// main_command_w: Handle command writes
//-------------------------------------------------
WRITE8_MEMBER( atari_jsa_base_device::main_command_w )
{
m_soundcomm->main_command_w(space, offset, data);
}
//-------------------------------------------------
// main_response_r: Handle response reads
//-------------------------------------------------
READ8_MEMBER( atari_jsa_base_device::main_response_r )
{
return m_soundcomm->main_response_r(space, offset);
}
//-------------------------------------------------
// sound_reset_w: Reset the sound board
//-------------------------------------------------
WRITE16_MEMBER( atari_jsa_base_device::sound_reset_w )
{
m_soundcomm->sound_reset_w(space, offset, data);
}
//-------------------------------------------------
// ym2151_port_w: Handle writes from the YM2151
// output port
//-------------------------------------------------
WRITE8_MEMBER( atari_jsa_base_device::ym2151_port_w )
{
m_ym2151_ct1 = (data >> 0) & 1;
m_ym2151_ct2 = (data >> 1) & 1;
update_all_volumes();
}
//-------------------------------------------------
// main_test_read_line: Return the state of the
// main's test line, provided by a callback
//-------------------------------------------------
READ_LINE_MEMBER(atari_jsa_base_device::main_test_read_line)
{
return !m_test_read_cb();
}
//-------------------------------------------------
// main_int_write_line: Forward interrupt signals
// from the comm device to the owning callback
//-------------------------------------------------
WRITE_LINE_MEMBER( atari_jsa_base_device::main_int_write_line )
{
m_main_int_cb(state);
}
//**************************************************************************
// BASE DEVICE CLASS FOR OKI6295-BASED VERSIONS
//**************************************************************************
//-------------------------------------------------
// atari_jsa_oki_base_device: Constructor
//-------------------------------------------------
atari_jsa_oki_base_device::atari_jsa_oki_base_device(const machine_config &mconfig, device_type devtype, const char *tag, device_t *owner, uint32_t clock, int channels)
: atari_jsa_base_device(mconfig, devtype, tag, owner, clock, channels),
m_oki1(*this, "oki1"),
m_oki2(*this, "oki2"),
m_oki1_region(*this, "oki1"),
m_oki2_region(*this, "oki2"),
m_oki1_banklo(*this, "oki1lo"),
m_oki1_bankhi(*this, "oki1hi"),
m_oki2_banklo(*this, "oki2lo"),
m_oki2_bankhi(*this, "oki2hi"),
m_oki6295_volume(1.0),
m_overall_volume(1.0)
{
}
//-------------------------------------------------
// oki_r: Handle reads from the OKI chip(s)
// on the JSA II, III, and IIIs boards
//-------------------------------------------------
READ8_MEMBER( atari_jsa_oki_base_device::oki_r )
{
// JSA IIIs selects the 2nd OKI via the low bit, so select it
if (m_oki2 != nullptr && offset == 1)
return m_oki2->read(space, offset);
// OKI may not be populated at all
else if (m_oki1 != nullptr)
return m_oki1->read(space, offset);
// if not present, return all 0xff
return 0xff;
}
//-------------------------------------------------
// oki_w: Handle writes to the OKI chip(s)
// on the JSA II, III, and IIIs boards
//-------------------------------------------------
WRITE8_MEMBER( atari_jsa_oki_base_device::oki_w )
{
// JSA IIIs selects the 2nd OKI via the low bit, so select it
if (m_oki2 != nullptr && offset == 1)
m_oki2->write(space, offset, data);
// OKI may not be populated at all
else if (m_oki1 != nullptr)
m_oki1->write(space, offset, data);
}
//-------------------------------------------------
// wrio_w: Handle writes to the general
// I/O port on JSA II, III, and IIIs boards
//-------------------------------------------------
WRITE8_MEMBER( atari_jsa_oki_base_device::wrio_w )
{
//
// 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 #1 bank bit 0 (JSA III/IIIs only)
// 0x01 = YM2151 reset (active low)
//
// update the bank
m_cpu_bank->set_entry((data >> 6) & 3);
// coin counters
machine().bookkeeping().coin_counter_w(1, (data >> 5) & 1);
machine().bookkeeping().coin_counter_w(0, (data >> 4) & 1);
// update the OKI frequency
if (m_oki1 != nullptr)
{
m_oki1->set_pin7(data & 8);
if ((data & 4) == 0)
m_oki1->reset();
}
// same for the 2nd OKI (JSA IIIs only)
if (m_oki2 != nullptr)
{
m_oki2->set_pin7(data & 8);
if ((data & 4) == 0)
m_oki2->reset();
}
// update the (left) OKI bank (JSA III/IIIs only)
if (m_oki1_banklo != nullptr)
m_oki1_banklo->set_entry((m_oki1_banklo->entry() & 2) | ((data >> 1) & 1));
// reset the YM2151 if needed
m_ym2151->reset_w(BIT(data, 0));
}
//-------------------------------------------------
// mix_w: Handle writes to the mixing
// register on JSA II, III, and IIIs boards
//-------------------------------------------------
WRITE8_MEMBER( atari_jsa_oki_base_device::mix_w )
{
//
// 0xc0 = right OKI6295 bank bits 0-1 (JSA IIIs only)
// 0x20 = low-pass filter enable
// 0x10 = OKI6295 #1 bank bit 1
// 0x0e = YM2151 volume (0-7)
// 0x01 = OKI6295 volume (0-1)
//
// update the right OKI bank (JSA IIIs only)
if (m_oki2_banklo != nullptr)
m_oki2_banklo->set_entry((data >> 6) & 3);
// update the (left) OKI bank (JSA III/IIIs only)
if (m_oki1_banklo != nullptr)
m_oki1_banklo->set_entry((m_oki1_banklo->entry() & 1) | ((data >> 3) & 2));
// update the volumes
m_ym2151_volume = ((data >> 1) & 7) / 7.0;
m_oki6295_volume = (data & 1) ? 1.0 : 0.5;
update_all_volumes();
}
//-------------------------------------------------
// overall_volume_w: Handle writes to control the
// total sound volume on JSA III/IIIs boards
//-------------------------------------------------
WRITE8_MEMBER( atari_jsa_oki_base_device::overall_volume_w )
{
m_overall_volume = data / 127.0;
update_all_volumes();
}
//-------------------------------------------------
// device_start: Start up the device
//-------------------------------------------------
void atari_jsa_oki_base_device::device_start()
{
// call the parent
atari_jsa_base_device::device_start();
// save states
save_item(NAME(m_oki6295_volume));
save_item(NAME(m_overall_volume));
// configure JSA III ADPCM banking
if (m_oki1_banklo.found() && m_oki1_bankhi.found() && m_oki1_region->bytes() >= 0x80000)
{
m_oki1_banklo->configure_entries(0, 2, m_oki1_region->base() + 0x00000, 0x00000);
m_oki1_banklo->configure_entries(2, 2, m_oki1_region->base() + 0x20000, 0x20000);
m_oki1_bankhi->set_base(m_oki1_region->base() + 0x60000);
}
// configure JSA IIIs ADPCM banking
if (m_oki2_banklo.found() && m_oki2_bankhi.found() && m_oki2_region->bytes() >= 0x80000)
{
m_oki2_banklo->configure_entries(0, 2, m_oki2_region->base() + 0x00000, 0x00000);
m_oki2_banklo->configure_entries(2, 2, m_oki2_region->base() + 0x20000, 0x20000);
m_oki2_bankhi->set_base(m_oki2_region->base() + 0x60000);
}
}
//-------------------------------------------------
// device_reset: Reset the device
//-------------------------------------------------
void atari_jsa_oki_base_device::device_reset()
{
// call the parent
atari_jsa_base_device::device_reset();
// reset the static states
m_oki6295_volume = 1.0;
m_overall_volume = 1.0;
update_all_volumes();
}
//-------------------------------------------------
// update_all_volumes: Update volumes for all
// chips
//-------------------------------------------------
void atari_jsa_oki_base_device::update_all_volumes()
{
if (m_oki1.found())
m_oki1->set_output_gain(ALL_OUTPUTS, m_overall_volume * m_oki6295_volume * m_ym2151_ct1);
if (m_oki2.found())
m_oki2->set_output_gain(ALL_OUTPUTS, m_overall_volume * m_oki6295_volume * m_ym2151_ct1);
m_ym2151->set_output_gain(ALL_OUTPUTS, m_overall_volume * m_ym2151_volume);
}
//**************************************************************************
// JSA I-SPECIFIC IMPLEMENTATION
//**************************************************************************
//-------------------------------------------------
// atari_jsa_i_device: Constructor
//-------------------------------------------------
atari_jsa_i_device::atari_jsa_i_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: atari_jsa_base_device(mconfig, ATARI_JSA_I, tag, owner, clock, 2),
m_pokey(*this, "pokey"),
m_tms5220(*this, "tms"),
m_jsai(*this, "JSAI"),
m_pokey_volume(1.0),
m_tms5220_volume(1.0)
{
}
//-------------------------------------------------
// rdio_r: Handle reads from the general I/O
// port on a JSA I board
//-------------------------------------------------
READ8_MEMBER( atari_jsa_i_device::rdio_r )
{
//
// 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
//
uint8_t result = m_jsai->read();
if (!m_test_read_cb())
result ^= 0x80;
if (m_tms5220 != nullptr && m_tms5220->readyq_r() == 0)
result |= 0x10;
else
result &= ~0x10;
return result;
}
//-------------------------------------------------
// wrio_w: Handle writes to the general I/O
// port on a JSA I board
//-------------------------------------------------
WRITE8_MEMBER( atari_jsa_i_device::wrio_w )
{
//
// 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)
//
// update the bank
m_cpu_bank->set_entry((data >> 6) & 3);
// coin counters
machine().bookkeeping().coin_counter_w(1, (data >> 5) & 1);
machine().bookkeeping().coin_counter_w(0, (data >> 4) & 1);
// handle TMS5220 I/O
if (m_tms5220 != nullptr)
{
int count = 5 | ((data >> 2) & 2);
m_tms5220->set_unscaled_clock(JSA_MASTER_CLOCK*2 / (16 - count));
m_tms5220->wsq_w((data >> 1) & 1);
m_tms5220->rsq_w((data >> 2) & 1);
}
// reset the YM2151 if needed
m_ym2151->reset_w(BIT(data, 0));
}
//-------------------------------------------------
// mix_w: Handle writes to the mixing register
// on a JSA I board
//-------------------------------------------------
WRITE8_MEMBER( atari_jsa_i_device::mix_w )
{
//
// 0xc0 = TMS5220 volume (0-3)
// 0x30 = POKEY volume (0-3)
// 0x0e = YM2151 volume (0-7)
// 0x01 = low-pass filter enable
//
m_tms5220_volume = ((data >> 6) & 3) / 3.0;
m_pokey_volume = ((data >> 4) & 3) / 3.0;
m_ym2151_volume = ((data >> 1) & 7) / 7.0;
update_all_volumes();
}
//-------------------------------------------------
// tms5220_voice: Handle writes to the TMS5220
// voice data register
//-------------------------------------------------
WRITE8_MEMBER( atari_jsa_i_device::tms5220_voice )
{
if (m_tms5220 != nullptr)
m_tms5220->data_w(space, 0, data);
}
//-------------------------------------------------
// pokey_r: Handle reads from the POKEY if
// present
//-------------------------------------------------
READ8_MEMBER( atari_jsa_i_device::pokey_r )
{
if (m_pokey != nullptr)
return m_pokey->read(space, offset);
return 0xff;
}
//-------------------------------------------------
// pokey_w: Handle writes to the POKEY if
// present
//-------------------------------------------------
WRITE8_MEMBER( atari_jsa_i_device::pokey_w )
{
if (m_pokey != nullptr)
m_pokey->write(space, offset, data);
}
//-------------------------------------------------
// device_add_mconfig - add device configuration
//-------------------------------------------------
// Fully populated JSA-I, not used by anyone
MACHINE_CONFIG_START(atari_jsa_i_device::device_add_mconfig)
// basic machine hardware
MCFG_DEVICE_ADD("cpu", M6502, JSA_MASTER_CLOCK/2)
MCFG_DEVICE_PROGRAM_MAP(atarijsa1_map)
MCFG_DEVICE_PERIODIC_INT_DEVICE("soundcomm", atari_sound_comm_device, sound_irq_gen, JSA_MASTER_CLOCK/4/16/16/14)
// sound hardware
MCFG_ATARI_SOUND_COMM_ADD("soundcomm", "cpu", WRITELINE(*this, atari_jsa_base_device, main_int_write_line))
MCFG_DEVICE_ADD("ym2151", YM2151, JSA_MASTER_CLOCK)
MCFG_YM2151_IRQ_HANDLER(WRITELINE("soundcomm", atari_sound_comm_device, ym2151_irq_gen))
MCFG_YM2151_PORT_WRITE_HANDLER(WRITE8(*this, atari_jsa_base_device, ym2151_port_w))
MCFG_MIXER_ROUTE(0, *this, 0.60, 0)
MCFG_MIXER_ROUTE(1, *this, 0.60, 1)
MCFG_DEVICE_ADD("pokey", POKEY, JSA_MASTER_CLOCK/2)
MCFG_MIXER_ROUTE(ALL_OUTPUTS, *this, 0.40, 0)
MCFG_MIXER_ROUTE(ALL_OUTPUTS, *this, 0.40, 1)
MCFG_DEVICE_ADD("tms", TMS5220C, JSA_MASTER_CLOCK*2/11) // potentially JSA_MASTER_CLOCK/9 as well
MCFG_MIXER_ROUTE(ALL_OUTPUTS, *this, 1.0, 0)
MCFG_MIXER_ROUTE(ALL_OUTPUTS, *this, 1.0, 1)
MACHINE_CONFIG_END
//-------------------------------------------------
// device_input_ports - return a pointer to
// the device's I/O ports
//-------------------------------------------------
ioport_constructor atari_jsa_i_device::device_input_ports() const
{
return INPUT_PORTS_NAME( jsa_i_ioports );
}
//-------------------------------------------------
// device_start: Start up the device
//-------------------------------------------------
void atari_jsa_i_device::device_start()
{
// call the parent
atari_jsa_base_device::device_start();
// save states
save_item(NAME(m_pokey_volume));
save_item(NAME(m_tms5220_volume));
}
//-------------------------------------------------
// device_reset: Reset the device
//-------------------------------------------------
void atari_jsa_i_device::device_reset()
{
// call the parent
atari_jsa_base_device::device_reset();
// reset the static states
m_pokey_volume = 1.0;
m_tms5220_volume = 1.0;
update_all_volumes();
}
//-------------------------------------------------
// update_all_volumes: Update volumes for all
// chips
//-------------------------------------------------
void atari_jsa_i_device::update_all_volumes()
{
if (m_tms5220 != nullptr)
m_tms5220->set_output_gain(ALL_OUTPUTS, m_tms5220_volume * m_ym2151_ct1);
if (m_pokey != nullptr)
m_pokey->set_output_gain(ALL_OUTPUTS, m_pokey_volume * m_ym2151_ct1);
m_ym2151->set_output_gain(ALL_OUTPUTS, m_ym2151_volume);
}
//**************************************************************************
// JSA II-SPECIFIC IMPLEMENTATION
//**************************************************************************
//-------------------------------------------------
// atari_jsa_ii_device: Constructor
//-------------------------------------------------
atari_jsa_ii_device::atari_jsa_ii_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: atari_jsa_oki_base_device(mconfig, ATARI_JSA_II, tag, owner, clock, 1)
, m_jsaii(*this, "JSAII")
{
}
//-------------------------------------------------
// rdio_r: Handle reads from the general I/O
// port on a JSA II board
//-------------------------------------------------
READ8_MEMBER( atari_jsa_ii_device::rdio_r )
{
//
// 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
//
uint8_t result = m_jsaii->read();
if (!m_test_read_cb())
result ^= 0x80;
return result;
}
//-------------------------------------------------
// device_add_mconfig - add device configuration
//-------------------------------------------------
// Fully populated JSA-II
MACHINE_CONFIG_START(atari_jsa_ii_device::device_add_mconfig)
// basic machine hardware
MCFG_DEVICE_ADD("cpu", M6502, JSA_MASTER_CLOCK/2)
MCFG_DEVICE_PROGRAM_MAP(atarijsa2_map)
MCFG_DEVICE_PERIODIC_INT_DEVICE("soundcomm", atari_sound_comm_device, sound_irq_gen, JSA_MASTER_CLOCK/4/16/16/14)
// sound hardware
MCFG_ATARI_SOUND_COMM_ADD("soundcomm", "cpu", WRITELINE(*this, atari_jsa_base_device, main_int_write_line))
MCFG_DEVICE_ADD("ym2151", YM2151, JSA_MASTER_CLOCK)
MCFG_YM2151_IRQ_HANDLER(WRITELINE("soundcomm", atari_sound_comm_device, ym2151_irq_gen))
MCFG_YM2151_PORT_WRITE_HANDLER(WRITE8(*this, atari_jsa_base_device, ym2151_port_w))
MCFG_MIXER_ROUTE(ALL_OUTPUTS, *this, 0.60, 0)
MCFG_DEVICE_ADD("oki1", OKIM6295, JSA_MASTER_CLOCK/3, okim6295_device::PIN7_HIGH)
MCFG_MIXER_ROUTE(ALL_OUTPUTS, *this, 0.75, 0)
MACHINE_CONFIG_END
//-------------------------------------------------
// device_input_ports - return a pointer to
// the device's I/O ports
//-------------------------------------------------
ioport_constructor atari_jsa_ii_device::device_input_ports() const
{
return INPUT_PORTS_NAME( jsa_ii_ioports );
}
//**************************************************************************
// JSA III-SPECIFIC IMPLEMENTATION
//**************************************************************************
//-------------------------------------------------
// atari_jsa_iii_device: Constructor
//-------------------------------------------------
atari_jsa_iii_device::atari_jsa_iii_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: atari_jsa_iii_device(mconfig, ATARI_JSA_III, tag, owner, clock, 1)
{
}
atari_jsa_iii_device::atari_jsa_iii_device(const machine_config &mconfig, device_type devtype, const char *tag, device_t *owner, uint32_t clock, int channels)
: atari_jsa_oki_base_device(mconfig, devtype, tag, owner, clock, channels)
, m_jsaiii(*this, "JSAIII")
{
}
//-------------------------------------------------
// jsa_iii_rdio: Handle reads from the general I/O
// port on a JSA III/IIIs board
//-------------------------------------------------
READ8_MEMBER( atari_jsa_iii_device::rdio_r )
{
//
// 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)
//
uint8_t result = m_jsaiii->read();
if (!m_test_read_cb())
result ^= 0x90;
return result;
}
//-------------------------------------------------
// device_add_mconfig - add device configuration
//-------------------------------------------------
// Fully populated JSA-III
MACHINE_CONFIG_START(atari_jsa_iii_device::device_add_mconfig)
// basic machine hardware
MCFG_DEVICE_ADD("cpu", M6502, JSA_MASTER_CLOCK/2)
MCFG_DEVICE_PROGRAM_MAP(atarijsa3_map)
MCFG_DEVICE_PERIODIC_INT_DEVICE("soundcomm", atari_sound_comm_device, sound_irq_gen, JSA_MASTER_CLOCK/4/16/16/14)
// sound hardware
MCFG_ATARI_SOUND_COMM_ADD("soundcomm", "cpu", WRITELINE(*this, atari_jsa_base_device, main_int_write_line))
MCFG_DEVICE_ADD("ym2151", YM2151, JSA_MASTER_CLOCK)
MCFG_YM2151_IRQ_HANDLER(WRITELINE("soundcomm", atari_sound_comm_device, ym2151_irq_gen))
MCFG_YM2151_PORT_WRITE_HANDLER(WRITE8(*this, atari_jsa_base_device, ym2151_port_w))
MCFG_MIXER_ROUTE(ALL_OUTPUTS, *this, 0.60, 0)
MCFG_DEVICE_ADD("oki1", OKIM6295, JSA_MASTER_CLOCK/3, okim6295_device::PIN7_HIGH)
MCFG_DEVICE_ADDRESS_MAP(0, jsa3_oki1_map)
MCFG_MIXER_ROUTE(ALL_OUTPUTS, *this, 0.75, 0)
MACHINE_CONFIG_END
//-------------------------------------------------
// device_input_ports - return a pointer to
// the device's I/O ports
//-------------------------------------------------
ioport_constructor atari_jsa_iii_device::device_input_ports() const
{
return INPUT_PORTS_NAME( jsa_iii_ioports );
}
//**************************************************************************
// JSA IIIS-SPECIFIC IMPLEMENTATION
//**************************************************************************
//-------------------------------------------------
// atari_jsa_iiis_device: Constructor
//-------------------------------------------------
atari_jsa_iiis_device::atari_jsa_iiis_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: atari_jsa_iii_device(mconfig, ATARI_JSA_IIIS, tag, owner, clock, 2)
{
}
//-------------------------------------------------
// device_add_mconfig - add device configuration
//-------------------------------------------------
// Fully populated JSA_IIIs
MACHINE_CONFIG_START(atari_jsa_iiis_device::device_add_mconfig)
atari_jsa_iii_device::device_add_mconfig(config);
MCFG_DEVICE_MODIFY("ym2151")
MCFG_SOUND_ROUTES_RESET()
MCFG_MIXER_ROUTE(0, *this, 0.60, 0)
MCFG_MIXER_ROUTE(1, *this, 0.60, 1)
MCFG_DEVICE_ADD("oki2", OKIM6295, JSA_MASTER_CLOCK/3, okim6295_device::PIN7_HIGH)
MCFG_MIXER_ROUTE(ALL_OUTPUTS, *this, 0.75, 1)
MCFG_DEVICE_ADDRESS_MAP(0, jsa3_oki2_map)
MACHINE_CONFIG_END