// license:BSD-3-Clause
// copyright-holders:Aaron Giles
/***************************************************************************
Cinematronics vector hardware
Special thanks to Neil Bradley, Zonn Moore, and Jeff Mitchell of the
Retrocade Alliance
Update:
6/27/99 Jim Hernandez -- 1st Attempt at Fixing Drone Star Castle sound and
pitch adjustments.
6/30/99 MLR added Rip Off, Solar Quest, Armor Attack (no samples yet)
11/04/08 Jim Hernandez -- Fixed Drone Star Castle sound again. It was
broken for a long time due to some changes.
Bugs: Sometimes the death explosion (small explosion) does not trigger.
***************************************************************************/
#include "emu.h"
#include "includes/cinemat.h"
#include "audio/nl_armora.h"
#include "audio/nl_barrier.h"
#include "audio/nl_boxingb.h"
#include "audio/nl_ripoff.h"
#include "audio/nl_solarq.h"
#include "audio/nl_spacewar.h"
#include "audio/nl_speedfrk.h"
#include "audio/nl_starcas.h"
#include "audio/nl_starhawk.h"
#include "audio/nl_sundance.h"
#include "audio/nl_tailg.h"
#include "audio/nl_warrior.h"
#include "cpu/z80/z80.h"
#include "machine/z80daisy.h"
#include "machine/z80ctc.h"
#include "speaker.h"
/*************************************
*
* Base class
*
*************************************/
cinemat_audio_device_base::cinemat_audio_device_base(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, u8 inputs_mask, void (*netlist)(netlist::nlparse_t &), double output_scale) :
device_t(mconfig, type, tag, owner, clock),
device_mixer_interface(mconfig, *this),
m_out_input(*this, "sound_nl:out_%u", 0),
m_inputs_mask(inputs_mask),
m_netlist(netlist),
m_output_scale(output_scale)
{
}
cinemat_audio_device_base &cinemat_audio_device_base::configure_latch_inputs(ls259_device &latch, u8 mask)
{
if (mask == 0)
mask = m_inputs_mask;
if (BIT(mask, 0))
latch.q_out_cb<0>().set(write_line_delegate(*this, FUNC(cinemat_audio_device_base::sound_w<0>)));
if (BIT(mask, 1))
latch.q_out_cb<1>().set(write_line_delegate(*this, FUNC(cinemat_audio_device_base::sound_w<1>)));
if (BIT(mask, 2))
latch.q_out_cb<2>().set(write_line_delegate(*this, FUNC(cinemat_audio_device_base::sound_w<2>)));
if (BIT(mask, 3))
latch.q_out_cb<3>().set(write_line_delegate(*this, FUNC(cinemat_audio_device_base::sound_w<3>)));
if (BIT(mask, 4))
latch.q_out_cb<4>().set(write_line_delegate(*this, FUNC(cinemat_audio_device_base::sound_w<4>)));
if (BIT(mask, 7))
latch.q_out_cb<7>().set(write_line_delegate(*this, FUNC(cinemat_audio_device_base::sound_w<7>)));
return *this;
}
void cinemat_audio_device_base::device_add_mconfig(machine_config &config)
{
NETLIST_SOUND(config, "sound_nl", 48000)
.set_source(m_netlist)
.add_route(ALL_OUTPUTS, *this, 1.0);
if ((m_inputs_mask & 0x01) != 0)
NETLIST_LOGIC_INPUT(config, m_out_input[0], "I_OUT_0.IN", 0);
if ((m_inputs_mask & 0x02) != 0)
NETLIST_LOGIC_INPUT(config, m_out_input[1], "I_OUT_1.IN", 0);
if ((m_inputs_mask & 0x04) != 0)
NETLIST_LOGIC_INPUT(config, m_out_input[2], "I_OUT_2.IN", 0);
if ((m_inputs_mask & 0x08) != 0)
NETLIST_LOGIC_INPUT(config, m_out_input[3], "I_OUT_3.IN", 0);
if ((m_inputs_mask & 0x10) != 0)
NETLIST_LOGIC_INPUT(config, m_out_input[4], "I_OUT_4.IN", 0);
if ((m_inputs_mask & 0x80) != 0)
NETLIST_LOGIC_INPUT(config, m_out_input[7], "I_OUT_7.IN", 0);
NETLIST_STREAM_OUTPUT(config, "sound_nl:cout0", 0, "OUTPUT").set_mult_offset(m_output_scale, 0.0);
}
void cinemat_audio_device_base::device_start()
{
save_item(NAME(m_inputs));
}
void cinemat_audio_device_base::input_set(int bit, int state)
{
u8 oldvals = m_inputs;
m_inputs = (m_inputs & ~(1 << bit)) | ((state & 1) << bit);
if (oldvals != m_inputs)
for (int index = 0; index < 8; index++)
if (m_out_input[index] != nullptr)
m_out_input[index]->write_line(BIT(m_inputs, index));
}
/*************************************
*
* Space Wars
*
*************************************/
DEFINE_DEVICE_TYPE(SPACE_WARS_AUDIO, spacewar_audio_device, "spacewar_audio", "Space Wars Sound Board")
spacewar_audio_device::spacewar_audio_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
: cinemat_audio_device_base(mconfig, SPACE_WARS_AUDIO, tag, owner, clock, 0x1f, NETLIST_NAME(spacewar), 4.5)
{
}
/*************************************
*
* Barrier
*
*************************************/
DEFINE_DEVICE_TYPE(BARRIER_AUDIO, barrier_audio_device, "barrier_audio", "Barrier Sound Board")
barrier_audio_device::barrier_audio_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
: cinemat_audio_device_base(mconfig, BARRIER_AUDIO, tag, owner, clock, 0x07, NETLIST_NAME(barrier), 6.1)
{
}
/*************************************
*
* Speed Freak
*
*************************************/
DEFINE_DEVICE_TYPE(SPEED_FREAK_AUDIO, speedfrk_audio_device, "speedfrk_audio", "Speed Freak Sound Board")
speedfrk_audio_device::speedfrk_audio_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
: cinemat_audio_device_base(mconfig, SPEED_FREAK_AUDIO, tag, owner, clock, 0x9f, NETLIST_NAME(speedfrk), 0.35)
{
}
/*************************************
*
* Star Hawk
*
*************************************/
DEFINE_DEVICE_TYPE(STAR_HAWK_AUDIO, starhawk_audio_device, "starhawk_audio", "Star Hawk Sound Board")
starhawk_audio_device::starhawk_audio_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
: cinemat_audio_device_base(mconfig, STAR_HAWK_AUDIO, tag, owner, clock, 0x9f, NETLIST_NAME(starhawk), 1.5)
{
}
/*************************************
*
* Sundance
*
*************************************/
DEFINE_DEVICE_TYPE(SUNDANCE_AUDIO, sundance_audio_device, "sundance_audio", "Sundance Sound Board")
sundance_audio_device::sundance_audio_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
: cinemat_audio_device_base(mconfig, SUNDANCE_AUDIO, tag, owner, clock, 0x9f, NETLIST_NAME(sundance), 1.0)
{
}
/*************************************
*
* Tail Gunner
*
*************************************/
DEFINE_DEVICE_TYPE(TAIL_GUNNER_AUDIO, tailg_audio_device, "tailg_audio", "Tail Gunner Sound Board")
tailg_audio_device::tailg_audio_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
: cinemat_audio_device_base(mconfig, TAIL_GUNNER_AUDIO, tag, owner, clock, 0x1f, NETLIST_NAME(tailg), 2.2)
{
}
/*************************************
*
* Warrior
*
*************************************/
DEFINE_DEVICE_TYPE(WARRIOR_AUDIO, warrior_audio_device, "warrior_audio", "Warrior Sound Board")
warrior_audio_device::warrior_audio_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
: cinemat_audio_device_base(mconfig, WARRIOR_AUDIO, tag, owner, clock, 0x1f, NETLIST_NAME(warrior), 1.5)
{
}
/*************************************
*
* Armor Attack
*
*************************************/
DEFINE_DEVICE_TYPE(ARMOR_ATTACK_AUDIO, armora_audio_device, "armora_audio", "Armor Atrack Sound Board")
armora_audio_device::armora_audio_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
: cinemat_audio_device_base(mconfig, ARMOR_ATTACK_AUDIO, tag, owner, clock, 0x9f, NETLIST_NAME(armora), 0.15)
{
}
/*************************************
*
* Ripoff
*
*************************************/
DEFINE_DEVICE_TYPE(RIPOFF_AUDIO, ripoff_audio_device, "ripoff_audio", "Rip Off Sound Board")
ripoff_audio_device::ripoff_audio_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
: cinemat_audio_device_base(mconfig, RIPOFF_AUDIO, tag, owner, clock, 0x9f, NETLIST_NAME(ripoff), 0.61)
{
}
/*************************************
*
* Star Castle
*
*************************************/
DEFINE_DEVICE_TYPE(STAR_CASTLE_AUDIO, starcas_audio_device, "starcas_audio", "Star Castle Sound Board")
starcas_audio_device::starcas_audio_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
: cinemat_audio_device_base(mconfig, STAR_CASTLE_AUDIO, tag, owner, clock, 0x9f, NETLIST_NAME(starcas), 0.15)
{
}
/*************************************
*
* Solar Quest
*
*************************************/
DEFINE_DEVICE_TYPE(SOLAR_QUEST_AUDIO, solarq_audio_device, "solarq_audio", "Solar Quest Sound Board")
solarq_audio_device::solarq_audio_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
: cinemat_audio_device_base(mconfig, SOLAR_QUEST_AUDIO, tag, owner, clock, 0x9f, NETLIST_NAME(solarq), 0.15)
{
}
/*************************************
*
* Boxing Bugs
*
*************************************/
DEFINE_DEVICE_TYPE(BOXING_BUGS_AUDIO, boxingb_audio_device, "boxingb_audio", "Boxing Bugs Sound Board")
boxingb_audio_device::boxingb_audio_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
: cinemat_audio_device_base(mconfig, BOXING_BUGS_AUDIO, tag, owner, clock, 0x9f, NETLIST_NAME(boxingb), 0.15)
{
}
/*************************************
*
* War of the Worlds
*
*************************************/
DEFINE_DEVICE_TYPE(WAR_OF_THE_WORLDS_AUDIO, wotw_audio_device, "wotw_audio", "War of the Worlds Sound Board")
wotw_audio_device::wotw_audio_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
: cinemat_audio_device_base(mconfig, WAR_OF_THE_WORLDS_AUDIO, tag, owner, clock, 0x9f, NETLIST_NAME(wotw), 0.15)
{
}
/*************************************
*
* Demon
*
*************************************/
TIMER_CALLBACK_MEMBER( demon_state::synced_sound_w )
{
m_sound_fifo[m_sound_fifo_in] = param;
m_sound_fifo_in = (m_sound_fifo_in + 1) % 16;
}
WRITE_LINE_MEMBER(demon_state::demon_sound4_w)
{
/* watch for a 0->1 edge on bit 4 ("shift in") to clock in the new data */
if (state)
machine().scheduler().synchronize(timer_expired_delegate(FUNC(demon_state::synced_sound_w), this), ~m_outlatch->output_state() & 0x0f);
}
u8 demon_state::sound_porta_r()
{
/* bits 0-3 are the sound data; bit 4 is the data ready */
return m_sound_fifo[m_sound_fifo_out] | ((m_sound_fifo_in != m_sound_fifo_out) << 4);
}
u8 demon_state::sound_portb_r()
{
return m_last_portb_write;
}
void demon_state::sound_portb_w(u8 data)
{
/* watch for a 0->1 edge on bit 0 ("shift out") to advance the data pointer */
if ((data & 1) != (m_last_portb_write & 1) && (data & 1) != 0)
m_sound_fifo_out = (m_sound_fifo_out + 1) % 16;
/* watch for a 0->1 edge of bit 1 ("hard reset") to reset the FIFO */
if ((data & 2) != (m_last_portb_write & 2) && (data & 2) != 0)
m_sound_fifo_in = m_sound_fifo_out = 0;
/* bit 2 controls the global mute */
if ((data & 4) != (m_last_portb_write & 4))
machine().sound().system_mute(data & 4);
/* remember the last value written */
m_last_portb_write = data;
}
void demon_state::sound_output_w(u8 data)
{
logerror("sound_output = %02X\n", data);
}
void demon_state::sound_start()
{
cinemat_state::sound_start();
/* register for save states */
save_item(NAME(m_sound_fifo));
save_item(NAME(m_sound_fifo_in));
save_item(NAME(m_sound_fifo_out));
save_item(NAME(m_last_portb_write));
}
void demon_state::sound_reset()
{
/* generic init */
cinemat_state::sound_reset();
/* reset the FIFO */
m_sound_fifo_in = m_sound_fifo_out = 0;
m_last_portb_write = 0xff;
/* turn off channel A on AY8910 #0 because it is used as a low-pass filter */
m_ay1->set_volume(0, 0);
}
void demon_state::demon_sound_map(address_map &map)
{
map(0x0000, 0x1fff).rom();
map(0x3000, 0x33ff).ram();
map(0x4000, 0x4001).r(m_ay1, FUNC(ay8910_device::data_r));
map(0x4002, 0x4003).w(m_ay1, FUNC(ay8910_device::data_address_w));
map(0x5000, 0x5001).r("ay2", FUNC(ay8910_device::data_r));
map(0x5002, 0x5003).w("ay2", FUNC(ay8910_device::data_address_w));
map(0x6000, 0x6001).r("ay3", FUNC(ay8910_device::data_r));
map(0x6002, 0x6003).w("ay3", FUNC(ay8910_device::data_address_w));
map(0x7000, 0x7000).nopw(); /* watchdog? */
}
void demon_state::demon_sound_ports(address_map &map)
{
map.global_mask(0xff);
map(0x00, 0x03).w("ctc", FUNC(z80ctc_device::write));
map(0x1c, 0x1f).w("ctc", FUNC(z80ctc_device::write));
}
static const z80_daisy_config daisy_chain[] =
{
{ "ctc" },
{ nullptr }
};
void demon_state::demon_sound(machine_config &config)
{
/* basic machine hardware */
z80_device& audiocpu(Z80(config, "audiocpu", 3579545));
audiocpu.set_daisy_config(daisy_chain);
audiocpu.set_addrmap(AS_PROGRAM, &demon_state::demon_sound_map);
audiocpu.set_addrmap(AS_IO, &demon_state::demon_sound_ports);
z80ctc_device& ctc(Z80CTC(config, "ctc", 3579545 /* same as "audiocpu" */));
ctc.intr_callback().set_inputline("audiocpu", INPUT_LINE_IRQ0);
m_outlatch->q_out_cb<4>().set(FUNC(demon_state::demon_sound4_w));
/* sound hardware */
SPEAKER(config, "mono").front_center();
AY8910(config, m_ay1, 3579545);
m_ay1->port_a_read_callback().set(FUNC(demon_state::sound_porta_r));
m_ay1->port_b_read_callback().set(FUNC(demon_state::sound_portb_r));
m_ay1->port_b_write_callback().set(FUNC(demon_state::sound_portb_w));
m_ay1->add_route(ALL_OUTPUTS, "mono", 0.25);
AY8910(config, "ay2", 3579545).add_route(ALL_OUTPUTS, "mono", 0.25);
ay8910_device &ay3(AY8910(config, "ay3", 3579545));
ay3.port_b_write_callback().set(FUNC(demon_state::sound_output_w));
ay3.add_route(ALL_OUTPUTS, "mono", 0.25);
}
/*************************************
*
* QB3
*
*************************************/
void qb3_state::qb3_sound_fifo_w(u8 data)
{
u16 rega = m_maincpu->state_int(ccpu_cpu_device::CCPU_A);
machine().scheduler().synchronize(timer_expired_delegate(FUNC(qb3_state::synced_sound_w), this), rega & 0x0f);
}
void qb3_state::sound_reset()
{
demon_state::sound_reset();
/* this patch prevents the sound ROM from eating itself when command $0A is sent */
/* on a cube rotate */
memregion("audiocpu")->base()[0x11dc] = 0x09;
}
void qb3_state::qb3_sound(machine_config &config)
{
demon_sound(config);
m_outlatch->q_out_cb<4>().set_nop(); // not mapped through LS259
}