// license:BSD-3-Clause // copyright-holders:Aaron Giles /*************************************************************************** williams.h Functions to emulate general the various Williams/Midway sound cards. **************************************************************************** Midway/Williams Audio Boards ---------------------------- 6809 MEMORY MAP Function Address R/W Data --------------------------------------------------------------- Program RAM 0000-07FF R/W D0-D7 Music (YM-2151) 2000-2001 R/W D0-D7 6821 PIA 4000-4003 R/W D0-D7 HC55516 clock low, digit latch 6000 W D0 HC55516 clock high 6800 W xx Bank select 7800 W D0-D2 Banked Program ROM 8000-FFFF R D0-D7 ****************************************************************************/ #include "emu.h" #include "williams.h" #include "machine/6821pia.h" #include "machine/rescap.h" #include "cpu/m6809/m6809.h" #include "cpu/m6800/m6800.h" #include "sound/dac.h" #include "sound/hc55516.h" #include "sound/okim6295.h" #include "sound/ymopm.h" #define NARC_MASTER_CLOCK XTAL(8'000'000) #define NARC_FM_CLOCK XTAL(3'579'545) #define CVSD_MASTER_CLOCK XTAL(8'000'000) #define CVSD_FM_CLOCK XTAL(3'579'545) #define ADPCM_MASTER_CLOCK XTAL(8'000'000) #define ADPCM_FM_CLOCK XTAL(3'579'545) //************************************************************************** // GLOBAL VARIABLES //************************************************************************** DEFINE_DEVICE_TYPE(WILLIAMS_CVSD_SOUND, williams_cvsd_sound_device, "wmscvsd", "Williams CVSD Sound Board") DEFINE_DEVICE_TYPE(WILLIAMS_NARC_SOUND, williams_narc_sound_device, "wmsnarc", "Williams NARC Sound Board") DEFINE_DEVICE_TYPE(WILLIAMS_ADPCM_SOUND, williams_adpcm_sound_device, "wmsadpcm", "Williams ADPCM Sound Board") DEFINE_DEVICE_TYPE(WILLIAMS_S4_SOUND, williams_s4_sound_device, "wmss4", "Williams System 4 Sound Board") DEFINE_DEVICE_TYPE(WILLIAMS_S6_SOUND, williams_s6_sound_device, "wmss6", "Williams System 6 Sound Board") DEFINE_DEVICE_TYPE(WILLIAMS_S9_SOUND, williams_s9_sound_device, "wmss9", "Williams System 9 Sound Board") DEFINE_DEVICE_TYPE(WILLIAMS_S11_SOUND, williams_s11_sound_device, "wmss11", "Williams System 11 Sound Board") //************************************************************************** // CVSD SOUND BOARD //************************************************************************** //------------------------------------------------- // williams_cvsd_sound_device - constructor //------------------------------------------------- williams_cvsd_sound_device::williams_cvsd_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) : device_t(mconfig, WILLIAMS_CVSD_SOUND, tag, owner, clock), device_mixer_interface(mconfig, *this), m_cpu(*this, "cpu"), m_pia(*this, "pia"), m_ym2151(*this, "ym2151"), m_hc55516(*this, "cvsd"), m_rombank(*this, "rombank"), m_talkback(0) { } //------------------------------------------------- // write - handle an external write to the input // latch //------------------------------------------------- void williams_cvsd_sound_device::write(u16 data) { m_sync_write_timer->adjust(attotime::zero, data); } //------------------------------------------------- // reset_write - write to the reset line //------------------------------------------------- WRITE_LINE_MEMBER(williams_cvsd_sound_device::reset_write) { // going high halts the CPU if (state) { bank_select_w(0); device_reset(); m_cpu->set_input_line(INPUT_LINE_RESET, ASSERT_LINE); } // going low resets and reactivates the CPU else m_cpu->set_input_line(INPUT_LINE_RESET, CLEAR_LINE); } //------------------------------------------------- // bank_select_w - change memory banks //------------------------------------------------- void williams_cvsd_sound_device::bank_select_w(u8 data) { m_rombank->set_entry(data & 0x0f); } //------------------------------------------------- // talkback_w - write to the talkback latch //------------------------------------------------- void williams_cvsd_sound_device::talkback_w(u8 data) { m_talkback = data; logerror("CVSD Talkback = %02X\n", data); } //------------------------------------------------- // cvsd_digit_clock_clear_w - clear the clock on // the HC55516 and clock the data //------------------------------------------------- void williams_cvsd_sound_device::cvsd_digit_clock_clear_w(u8 data) { m_hc55516->clock_w(0); m_hc55516->digit_w(data&1); } //------------------------------------------------- // cvsd_clock_set_w - set the clock on the HC55516 //------------------------------------------------- void williams_cvsd_sound_device::cvsd_clock_set_w(u8 data) { m_hc55516->clock_w(1); } //------------------------------------------------- // audio CPU map //------------------------------------------------- void williams_cvsd_sound_device::williams_cvsd_map(address_map &map) { map(0x0000, 0x07ff).mirror(0x1800).ram(); map(0x2000, 0x2001).mirror(0x1ffe).rw("ym2151", FUNC(ym2151_device::read), FUNC(ym2151_device::write)); map(0x4000, 0x4003).mirror(0x1ffc).rw("pia", FUNC(pia6821_device::read), FUNC(pia6821_device::write)); map(0x6000, 0x6000).mirror(0x07ff).w(FUNC(williams_cvsd_sound_device::cvsd_digit_clock_clear_w)); map(0x6800, 0x6800).mirror(0x07ff).w(FUNC(williams_cvsd_sound_device::cvsd_clock_set_w)); map(0x7800, 0x7800).mirror(0x07ff).w(FUNC(williams_cvsd_sound_device::bank_select_w)); map(0x8000, 0xffff).bankr("rombank"); } //------------------------------------------------- // device_add_mconfig - add device configuration //------------------------------------------------- void williams_cvsd_sound_device::device_add_mconfig(machine_config &config) { MC6809E(config, m_cpu, CVSD_MASTER_CLOCK / 4); m_cpu->set_addrmap(AS_PROGRAM, &williams_cvsd_sound_device::williams_cvsd_map); PIA6821(config, m_pia, 0); m_pia->writepa_handler().set("dac", FUNC(dac_byte_interface::data_w)); m_pia->writepb_handler().set(FUNC(williams_cvsd_sound_device::talkback_w)); m_pia->ca2_handler().set(m_ym2151, FUNC(ym2151_device::reset_w)); m_pia->irqa_handler().set_inputline(m_cpu, M6809_FIRQ_LINE); m_pia->irqb_handler().set_inputline(m_cpu, INPUT_LINE_NMI); YM2151(config, m_ym2151, CVSD_FM_CLOCK); m_ym2151->irq_handler().set(m_pia, FUNC(pia6821_device::ca1_w)).invert(); // IRQ is not true state m_ym2151->add_route(ALL_OUTPUTS, *this, 0.10); MC1408(config, "dac", 0).add_route(ALL_OUTPUTS, *this, 0.25); HC55516(config, m_hc55516, 0); m_hc55516->add_route(ALL_OUTPUTS, *this, 0.60); } //------------------------------------------------- // device_start - device-specific startup //------------------------------------------------- void williams_cvsd_sound_device::device_start() { // configure master CPU banks u8 *rom = memregion("cpu")->base(); for (int bank = 0; bank < 16; bank++) { // // D0/D1 -> selects: 0=U4 1=U19 2=U20 3=n/c // D2 -> A15 // D3 -> A16 // offs_t offset = 0x8000 * ((bank >> 2) & 3) + 0x20000 * (bank & 3); m_rombank->configure_entry(bank, &rom[0x10000 + offset]); } m_rombank->set_entry(0); // reset the IRQ state m_pia->ca1_w(1); // register for save states save_item(NAME(m_talkback)); // allocate timers m_sync_write_timer = timer_alloc(FUNC(williams_cvsd_sound_device::sync_write), this); } //------------------------------------------------- // device_reset - device-specific reset //------------------------------------------------- void williams_cvsd_sound_device::device_reset() { // reset interrupt states m_cpu->set_input_line(M6809_FIRQ_LINE, CLEAR_LINE); m_cpu->set_input_line(M6809_IRQ_LINE, CLEAR_LINE); m_cpu->set_input_line(INPUT_LINE_NMI, CLEAR_LINE); } //------------------------------------------------- // sync_write //------------------------------------------------- TIMER_CALLBACK_MEMBER(williams_cvsd_sound_device::sync_write) { // process incoming data write m_pia->portb_w(param & 0xff); m_pia->cb1_w((param >> 8) & 1); m_pia->cb2_w((param >> 9) & 1); } //************************************************************************** // NARC SOUND BOARD //************************************************************************** //------------------------------------------------- // williams_narc_sound_device - constructor //------------------------------------------------- williams_narc_sound_device::williams_narc_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) : device_t(mconfig, WILLIAMS_NARC_SOUND, tag, owner, clock), device_mixer_interface(mconfig, *this), m_cpu(*this, "cpu%u", 0U), m_hc55516(*this, "cvsd"), m_masterbank(*this, "masterbank"), m_slavebank(*this, "slavebank"), m_latch(0), m_latch2(0), m_talkback(0), m_audio_sync(0), m_sound_int_state(0) { } //------------------------------------------------- // read - return the talkback register with the // SYNC bits in bits 8 and 9 //------------------------------------------------- u16 williams_narc_sound_device::read() { return m_talkback | (m_audio_sync << 8); } //------------------------------------------------- // write - handle an external write to the input // latch //------------------------------------------------- void williams_narc_sound_device::write(u16 data) { m_sync_master_timer->adjust(attotime::zero, data); } //------------------------------------------------- // reset_write - write to the reset line //------------------------------------------------- WRITE_LINE_MEMBER(williams_narc_sound_device::reset_write) { // going high halts the CPU if (state) { master_bank_select_w(0); slave_bank_select_w(0); device_reset(); m_cpu[0]->set_input_line(INPUT_LINE_RESET, ASSERT_LINE); m_cpu[1]->set_input_line(INPUT_LINE_RESET, ASSERT_LINE); } // going low resets and reactivates the CPU else { m_cpu[0]->set_input_line(INPUT_LINE_RESET, CLEAR_LINE); m_cpu[1]->set_input_line(INPUT_LINE_RESET, CLEAR_LINE); } } //------------------------------------------------- // master_bank_select_w - select the bank for the // master CPU //------------------------------------------------- void williams_narc_sound_device::master_bank_select_w(u8 data) { m_masterbank->set_entry(data & 0x0f); } //------------------------------------------------- // slave_bank_select_w - select the bank for the // slave CPU //------------------------------------------------- void williams_narc_sound_device::slave_bank_select_w(u8 data) { m_slavebank->set_entry(data & 0x0f); } //------------------------------------------------- // command_r - read command written by external // agent //------------------------------------------------- u8 williams_narc_sound_device::command_r() { m_cpu[0]->set_input_line(M6809_IRQ_LINE, CLEAR_LINE); m_sound_int_state = 0; return m_latch; } //------------------------------------------------- // command2_w - write command from master CPU to // slave CPU //------------------------------------------------- void williams_narc_sound_device::command2_w(u8 data) { m_sync_slave_timer->adjust(attotime::zero, data); } //------------------------------------------------- // command2_r - read command written by master // CPU //------------------------------------------------- u8 williams_narc_sound_device::command2_r() { m_cpu[1]->set_input_line(M6809_FIRQ_LINE, CLEAR_LINE); return m_latch2; } //------------------------------------------------- // master_talkback_w - handle writes to the // talkback latch from the master CPU //------------------------------------------------- void williams_narc_sound_device::master_talkback_w(u8 data) { m_talkback = data; logerror("Master Talkback = %02X\n", data); } //------------------------------------------------- // master_sync_w - handle writes to the master // SYNC register //------------------------------------------------- void williams_narc_sound_device::master_sync_w(u8 data) { m_sync_clear_timer->adjust(attotime::from_double(TIME_OF_74LS123(180000, 0.000001)), 0x01); m_audio_sync |= 0x01; logerror("Master sync = %02X\n", data); } //------------------------------------------------- // slave_talkback_w - handle writes to the // talkback latch from the slave CPU //------------------------------------------------- void williams_narc_sound_device::slave_talkback_w(u8 data) { logerror("Slave Talkback = %02X\n", data); } //------------------------------------------------- // slave_sync_w - handle writes to the slave // SYNC register //------------------------------------------------- void williams_narc_sound_device::slave_sync_w(u8 data) { m_sync_clear_timer->adjust(attotime::from_double(TIME_OF_74LS123(180000, 0.000001)), 0x02); m_audio_sync |= 0x02; logerror("Slave sync = %02X\n", data); } //------------------------------------------------- // cvsd_digit_clock_clear_w - clear the clk pin on // the HC555xx and clock the data latch //------------------------------------------------- void williams_narc_sound_device::cvsd_digit_clock_clear_w(u8 data) { m_hc55516->clock_w(0); m_hc55516->digit_w(data&1); } //--------------------------------------------------- // cvsd_clock_set_w - set the clk pin on the HC555xx //--------------------------------------------------- void williams_narc_sound_device::cvsd_clock_set_w(u8 data) { m_hc55516->clock_w(1); } //------------------------------------------------- // master CPU map //------------------------------------------------- void williams_narc_sound_device::williams_narc_master_map(address_map &map) { map(0x0000, 0x1fff).ram(); map(0x2000, 0x2001).mirror(0x03fe).rw("ym2151", FUNC(ym2151_device::read), FUNC(ym2151_device::write)); map(0x2800, 0x2800).mirror(0x03ff).w(FUNC(williams_narc_sound_device::master_talkback_w)); map(0x2c00, 0x2c00).mirror(0x03ff).w(FUNC(williams_narc_sound_device::command2_w)); map(0x3000, 0x3000).mirror(0x03ff).w("dac1", FUNC(dac_byte_interface::data_w)); map(0x3400, 0x3400).mirror(0x03ff).r(FUNC(williams_narc_sound_device::command_r)); map(0x3800, 0x3800).mirror(0x03ff).w(FUNC(williams_narc_sound_device::master_bank_select_w)); map(0x3c00, 0x3c00).mirror(0x03ff).w(FUNC(williams_narc_sound_device::master_sync_w)); map(0x4000, 0xbfff).bankr("masterbank"); map(0xc000, 0xffff).bankr("masterupper"); } //------------------------------------------------- // slave CPU map //------------------------------------------------- void williams_narc_sound_device::williams_narc_slave_map(address_map &map) { map(0x0000, 0x1fff).ram(); map(0x2000, 0x2000).mirror(0x03ff).w(FUNC(williams_narc_sound_device::cvsd_clock_set_w)); map(0x2400, 0x2400).mirror(0x03ff).w(FUNC(williams_narc_sound_device::cvsd_digit_clock_clear_w)); map(0x2800, 0x2800).mirror(0x03ff).w(FUNC(williams_narc_sound_device::slave_talkback_w)); map(0x3000, 0x3000).mirror(0x03ff).w("dac2", FUNC(dac_byte_interface::data_w)); map(0x3400, 0x3400).mirror(0x03ff).r(FUNC(williams_narc_sound_device::command2_r)); map(0x3800, 0x3800).mirror(0x03ff).w(FUNC(williams_narc_sound_device::slave_bank_select_w)); map(0x3c00, 0x3c00).mirror(0x03ff).w(FUNC(williams_narc_sound_device::slave_sync_w)); map(0x4000, 0xbfff).bankr("slavebank"); map(0xc000, 0xffff).bankr("slaveupper"); } //------------------------------------------------- // device_add_mconfig - add device configuration //------------------------------------------------- void williams_narc_sound_device::device_add_mconfig(machine_config &config) { MC6809E(config, m_cpu[0], NARC_MASTER_CLOCK / 4); m_cpu[0]->set_addrmap(AS_PROGRAM, &williams_narc_sound_device::williams_narc_master_map); MC6809E(config, m_cpu[1], NARC_MASTER_CLOCK / 4); m_cpu[1]->set_addrmap(AS_PROGRAM, &williams_narc_sound_device::williams_narc_slave_map); ym2151_device &ym2151(YM2151(config, "ym2151", NARC_FM_CLOCK)); ym2151.irq_handler().set_inputline("cpu0", M6809_FIRQ_LINE); ym2151.add_route(ALL_OUTPUTS, *this, 0.10); AD7224(config, "dac1", 0).add_route(ALL_OUTPUTS, *this, 0.25); AD7224(config, "dac2", 0).add_route(ALL_OUTPUTS, *this, 0.25); HC55516(config, m_hc55516, 0).add_route(ALL_OUTPUTS, *this, 0.60); } //------------------------------------------------- // device_start - device-specific startup //------------------------------------------------- void williams_narc_sound_device::device_start() { // configure master CPU banks u8 *rom = memregion("cpu0")->base(); for (int bank = 0; bank < 16; bank++) { // // D0 -> A15 // D1/D2 -> selects: 0=n/c 1=U3 2=U4 3=U5 // D3 -> A16 // offs_t offset = 0x8000 * (bank & 1) + 0x10000 * ((bank >> 3) & 1) + 0x20000 * ((bank >> 1) & 3); m_masterbank->configure_entry(bank, &rom[0x10000 + offset]); } membank("masterupper")->set_base(&rom[0x10000 + 0x4000 + 0x8000 + 0x10000 + 0x20000 * 3]); // configure slave CPU banks rom = memregion("cpu1")->base(); for (int bank = 0; bank < 16; bank++) { // // D0 -> A15 // D1/D2 -> selects: 0=U35 1=U36 2=U37 3=U38 // D3 -> A16 // offs_t offset = 0x8000 * (bank & 1) + 0x10000 * ((bank >> 3) & 1) + 0x20000 * ((bank >> 1) & 3); m_slavebank->configure_entry(bank, &rom[0x10000 + offset]); } membank("slaveupper")->set_base(&rom[0x10000 + 0x4000 + 0x8000 + 0x10000 + 0x20000 * 3]); // register for save states save_item(NAME(m_latch)); save_item(NAME(m_latch2)); save_item(NAME(m_talkback)); save_item(NAME(m_audio_sync)); save_item(NAME(m_sound_int_state)); // allocate timers m_sync_master_timer = timer_alloc(FUNC(williams_narc_sound_device::sync_master_command), this); m_sync_slave_timer = timer_alloc(FUNC(williams_narc_sound_device::sync_slave_command), this); m_sync_clear_timer = timer_alloc(FUNC(williams_narc_sound_device::sync_clear), this); } //------------------------------------------------- // device_reset - device-specific reset //------------------------------------------------- void williams_narc_sound_device::device_reset() { // reset interrupt states m_sound_int_state = 0; m_cpu[0]->set_input_line(M6809_FIRQ_LINE, CLEAR_LINE); m_cpu[0]->set_input_line(M6809_IRQ_LINE, CLEAR_LINE); m_cpu[0]->set_input_line(INPUT_LINE_NMI, CLEAR_LINE); m_cpu[1]->set_input_line(M6809_FIRQ_LINE, CLEAR_LINE); m_cpu[1]->set_input_line(M6809_IRQ_LINE, CLEAR_LINE); m_cpu[1]->set_input_line(INPUT_LINE_NMI, CLEAR_LINE); } //------------------------------------------------- // timer callbacks //------------------------------------------------- TIMER_CALLBACK_MEMBER(williams_narc_sound_device::sync_master_command) { m_latch = param & 0xff; m_cpu[0]->set_input_line(INPUT_LINE_NMI, (param & 0x100) ? CLEAR_LINE : ASSERT_LINE); if ((param & 0x200) == 0) { m_cpu[0]->set_input_line(M6809_IRQ_LINE, ASSERT_LINE); m_sound_int_state = 1; } } TIMER_CALLBACK_MEMBER(williams_narc_sound_device::sync_slave_command) { m_latch2 = param & 0xff; m_cpu[1]->set_input_line(M6809_FIRQ_LINE, ASSERT_LINE); } TIMER_CALLBACK_MEMBER(williams_narc_sound_device::sync_clear) { m_audio_sync &= ~param; } //************************************************************************** // ADPCM SOUND BOARD //************************************************************************** //------------------------------------------------- // williams_adpcm_sound_device - constructor //------------------------------------------------- williams_adpcm_sound_device::williams_adpcm_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) : device_t(mconfig, WILLIAMS_ADPCM_SOUND, tag, owner, clock), device_mixer_interface(mconfig, *this), m_cpu(*this, "cpu"), m_rombank(*this, "rombank"), m_okibank(*this, "okibank"), m_latch(0), m_talkback(0), m_sound_int_state(0) { } //------------------------------------------------- // write - handle an external write to the input // latch //------------------------------------------------- void williams_adpcm_sound_device::write(u16 data) { m_sync_command_timer->adjust(attotime::zero, data); } //------------------------------------------------- // reset_write - write to the reset line //------------------------------------------------- WRITE_LINE_MEMBER(williams_adpcm_sound_device::reset_write) { // going high halts the CPU if (state) { bank_select_w(0); device_reset(); m_cpu->set_input_line(INPUT_LINE_RESET, ASSERT_LINE); } // going low resets and reactivates the CPU else m_cpu->set_input_line(INPUT_LINE_RESET, CLEAR_LINE); } //------------------------------------------------- // irq_read - read the sound IRQ state //------------------------------------------------- READ_LINE_MEMBER(williams_adpcm_sound_device::irq_read) { return m_sound_int_state; } //------------------------------------------------- // bank_select_w - select the sound CPU memory // bank //------------------------------------------------- void williams_adpcm_sound_device::bank_select_w(u8 data) { m_rombank->set_entry(data & 0x07); } //------------------------------------------------- // oki6295_bank_select_w - select the OKI6295 // memory bank //------------------------------------------------- void williams_adpcm_sound_device::oki6295_bank_select_w(u8 data) { m_okibank->set_entry(data & 7); } //------------------------------------------------- // command_r - read the command from the external // latch //------------------------------------------------- u8 williams_adpcm_sound_device::command_r() { m_cpu->set_input_line(M6809_IRQ_LINE, CLEAR_LINE); // don't clear the external IRQ state for a short while; this allows the // self-tests to pass m_irq_clear_timer->adjust(attotime::from_usec(10)); return m_latch; } //------------------------------------------------- // talkback_w - write to the talkback latch //------------------------------------------------- void williams_adpcm_sound_device::talkback_w(u8 data) { m_talkback = data; logerror("ADPCM Talkback = %02X\n", data); } //------------------------------------------------- // audio CPU map //------------------------------------------------- void williams_adpcm_sound_device::williams_adpcm_map(address_map &map) { map(0x0000, 0x1fff).ram(); map(0x2000, 0x2000).mirror(0x03ff).w(FUNC(williams_adpcm_sound_device::bank_select_w)); map(0x2400, 0x2401).mirror(0x03fe).rw("ym2151", FUNC(ym2151_device::read), FUNC(ym2151_device::write)); map(0x2800, 0x2800).mirror(0x03ff).w("dac", FUNC(dac_byte_interface::data_w)); map(0x2c00, 0x2c00).mirror(0x03ff).rw("oki", FUNC(okim6295_device::read), FUNC(okim6295_device::write)); map(0x3000, 0x3000).mirror(0x03ff).r(FUNC(williams_adpcm_sound_device::command_r)); map(0x3400, 0x3400).mirror(0x03ff).w(FUNC(williams_adpcm_sound_device::oki6295_bank_select_w)); map(0x3c00, 0x3c00).mirror(0x03ff).w(FUNC(williams_adpcm_sound_device::talkback_w)); map(0x4000, 0xbfff).bankr("rombank"); map(0xc000, 0xffff).bankr("romupper"); } //------------------------------------------------- // OKI6295 map //------------------------------------------------- void williams_adpcm_sound_device::williams_adpcm_oki_map(address_map &map) { map(0x00000, 0x1ffff).bankr("okibank"); map(0x20000, 0x3ffff).rom().region("oki", 0x60000); } //------------------------------------------------- // device_add_mconfig - add device configuration //------------------------------------------------- void williams_adpcm_sound_device::device_add_mconfig(machine_config &config) { MC6809E(config, m_cpu, ADPCM_MASTER_CLOCK / 4); m_cpu->set_addrmap(AS_PROGRAM, &williams_adpcm_sound_device::williams_adpcm_map); ym2151_device &ym2151(YM2151(config, "ym2151", ADPCM_FM_CLOCK)); ym2151.irq_handler().set_inputline("cpu", M6809_FIRQ_LINE); ym2151.add_route(ALL_OUTPUTS, *this, 0.10); AD7524(config, "dac", 0).add_route(ALL_OUTPUTS, *this, 0.10); okim6295_device &oki(OKIM6295(config, "oki", ADPCM_MASTER_CLOCK/8, okim6295_device::PIN7_HIGH)); // clock frequency & pin 7 not verified oki.set_addrmap(0, &williams_adpcm_sound_device::williams_adpcm_oki_map); oki.add_route(ALL_OUTPUTS, *this, 0.15); } //------------------------------------------------- // device_start - device-specific startup //------------------------------------------------- void williams_adpcm_sound_device::device_start() { // configure banks u8 *rom = memregion("cpu")->base(); m_rombank->configure_entries(0, 8, &rom[0x10000], 0x8000); membank("romupper")->set_base(&rom[0x10000 + 0x4000 + 7 * 0x8000]); // expand ADPCM data rom = memregion("oki")->base(); // it is assumed that U12 is loaded @ 0x00000 and U13 is loaded @ 0x40000 m_okibank->configure_entry(0, &rom[0x40000]); m_okibank->configure_entry(1, &rom[0x40000]); m_okibank->configure_entry(2, &rom[0x20000]); m_okibank->configure_entry(3, &rom[0x00000]); m_okibank->configure_entry(4, &rom[0xe0000]); m_okibank->configure_entry(5, &rom[0xc0000]); m_okibank->configure_entry(6, &rom[0xa0000]); m_okibank->configure_entry(7, &rom[0x80000]); // register for save states save_item(NAME(m_latch)); save_item(NAME(m_talkback)); save_item(NAME(m_sound_int_state)); m_sync_command_timer = timer_alloc(FUNC(williams_adpcm_sound_device::sync_command), this); m_irq_clear_timer = timer_alloc(FUNC(williams_adpcm_sound_device::irq_clear), this); } //------------------------------------------------- // device_reset - device-specific reset //------------------------------------------------- void williams_adpcm_sound_device::device_reset() { // reset interrupt states m_sound_int_state = 0; m_cpu->set_input_line(M6809_FIRQ_LINE, CLEAR_LINE); m_cpu->set_input_line(M6809_IRQ_LINE, CLEAR_LINE); m_cpu->set_input_line(INPUT_LINE_NMI, CLEAR_LINE); m_sync_command_timer->adjust(attotime::never); m_irq_clear_timer->adjust(attotime::never); } //------------------------------------------------- // timer callbacks //------------------------------------------------- TIMER_CALLBACK_MEMBER(williams_adpcm_sound_device::sync_command) { m_latch = param & 0xff; if (!(param & 0x200)) { m_cpu->set_input_line(M6809_IRQ_LINE, ASSERT_LINE); m_sound_int_state = 1; machine().scheduler().boost_interleave(attotime::zero, attotime::from_usec(100)); } } TIMER_CALLBACK_MEMBER(williams_adpcm_sound_device::irq_clear) { m_sound_int_state = 0; } //************************************************************************** // S4 SOUND BOARD (simple sound card used in system 3/4 pinballs) //************************************************************************** //------------------------------------------------- // williams_s4_sound_device - constructor //------------------------------------------------- williams_s4_sound_device::williams_s4_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) : device_t(mconfig, WILLIAMS_S4_SOUND, tag, owner, clock) , device_mixer_interface(mconfig, *this) , m_cpu(*this, "cpu") , m_pia(*this, "pia") { } //------------------------------------------------- // write - handle an external write //------------------------------------------------- void williams_s4_sound_device::write(u8 data) { // Handle S2 (electronic or tones) data &= ioport("S4")->read(); if ((data & 0x9f) != 0x9f) { m_pia->portb_w(data); m_pia->cb1_w(0); } m_pia->cb1_w(1); } //------------------------------------------------- // audio CPU map //------------------------------------------------- void williams_s4_sound_device::williams_s4_map(address_map &map) { map.global_mask(0x0fff); map(0x0000, 0x00ff).ram(); map(0x0400, 0x0403).rw(m_pia, FUNC(pia6821_device::read), FUNC(pia6821_device::write)); map(0x0800, 0x0fff).rom().region("audiocpu", 0); } //------------------------------------------------- // device_add_mconfig - add device configuration //------------------------------------------------- void williams_s4_sound_device::device_add_mconfig(machine_config &config) { M6808(config, m_cpu, 3580000); m_cpu->set_addrmap(AS_PROGRAM, &williams_s4_sound_device::williams_s4_map); MC1408(config, "dac", 0).add_route(ALL_OUTPUTS, *this, 0.5); PIA6821(config, m_pia, 0); m_pia->writepa_handler().set("dac", FUNC(dac_byte_interface::data_w)); m_pia->irqa_handler().set_inputline(m_cpu, M6808_IRQ_LINE); m_pia->irqb_handler().set_inputline(m_cpu, M6808_IRQ_LINE); } //------------------------------------------------- // device_start - device-specific startup //------------------------------------------------- void williams_s4_sound_device::device_start() { // register for save states save_item(NAME(m_dummy)); } //------------------------------------------------- // device_reset - device-specific reset //------------------------------------------------- void williams_s4_sound_device::device_reset() { // reset interrupt states m_cpu->set_input_line(M6808_IRQ_LINE, CLEAR_LINE); } INPUT_PORTS_START( williams_s4 ) PORT_START("S4") PORT_BIT( 0xbf, IP_ACTIVE_LOW, IPT_UNUSED) PORT_DIPNAME( 0x40, 0x00, "Sounds" ) PORT_DIPSETTING( 0x00, "Set 1" ) PORT_DIPSETTING( 0x40, "Set 2" ) PORT_BIT( 0x100, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Audio Diag") PORT_CODE(KEYCODE_9_PAD) PORT_CHANGED_MEMBER(DEVICE_SELF, williams_s4_sound_device, audio_nmi, 1) INPUT_PORTS_END INPUT_CHANGED_MEMBER( williams_s4_sound_device::audio_nmi ) { // Diagnostic button sends a pulse to NMI pin if (newval==CLEAR_LINE) m_cpu->pulse_input_line(INPUT_LINE_NMI, attotime::zero); } //------------------------------------------------- // device_input_ports - return a pointer to // the device's I/O ports //------------------------------------------------- ioport_constructor williams_s4_sound_device::device_input_ports() const { return INPUT_PORTS_NAME( williams_s4 ); } //************************************************************************** // S6 SOUND BOARD (s4 with speech, used in system 6/6a/7 pinballs) //************************************************************************** //------------------------------------------------- // williams_s6_sound_device - constructor //------------------------------------------------- williams_s6_sound_device::williams_s6_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) : device_t(mconfig, WILLIAMS_S6_SOUND, tag, owner, clock) , device_mixer_interface(mconfig, *this) , m_cpu(*this, "cpu") , m_pia(*this, "pia") , m_hc(*this, "hc") { } //------------------------------------------------- // write - handle an external write //------------------------------------------------- void williams_s6_sound_device::write(u8 data) { data = bitswap<8>(data, 6, 7, 5, 4, 3, 2, 1, 0) | 0x40; // Handle dips data &= ioport("S6")->read(); if ((data & 0x9f) != 0x9f) { m_pia->portb_w(data); m_pia->cb1_w(0); } m_pia->cb1_w(1); } //------------------------------------------------- // pb_w - acknowledge interrupt //------------------------------------------------- void williams_s6_sound_device::pb_w(u8 data) { m_pia->cb1_w(1); } //------------------------------------------------- // audio CPU map //------------------------------------------------- void williams_s6_sound_device::williams_s6_map(address_map &map) { map.global_mask(0x7fff); map(0x0000, 0x00ff).ram(); map(0x0400, 0x0403).rw(m_pia, FUNC(pia6821_device::read), FUNC(pia6821_device::write)); map(0x3000, 0x7fff).rom().region("audiocpu", 0); } //------------------------------------------------- // device_add_mconfig - add device configuration //------------------------------------------------- void williams_s6_sound_device::device_add_mconfig(machine_config &config) { M6802(config, m_cpu, 3580000); m_cpu->set_addrmap(AS_PROGRAM, &williams_s6_sound_device::williams_s6_map); MC1408(config, "dac", 0).add_route(ALL_OUTPUTS, *this, 0.5); HC55516(config, m_hc, 0).add_route(ALL_OUTPUTS, *this, 1.00); PIA6821(config, m_pia, 0); m_pia->writepa_handler().set("dac", FUNC(dac_byte_interface::data_w)); m_pia->writepb_handler().set(FUNC(williams_s6_sound_device::pb_w)); m_pia->ca2_handler().set(m_hc, FUNC(hc55516_device::digit_w)); m_pia->cb2_handler().set(m_hc, FUNC(hc55516_device::clock_w)); m_pia->irqa_handler().set_inputline(m_cpu, M6802_IRQ_LINE); m_pia->irqb_handler().set_inputline(m_cpu, M6802_IRQ_LINE); } //------------------------------------------------- // device_start - device-specific startup //------------------------------------------------- void williams_s6_sound_device::device_start() { // register for save states save_item(NAME(m_dummy)); } //------------------------------------------------- // device_reset - device-specific reset //------------------------------------------------- void williams_s6_sound_device::device_reset() { // reset interrupt states m_cpu->set_input_line(M6808_IRQ_LINE, CLEAR_LINE); } INPUT_PORTS_START( williams_s6 ) PORT_START("S6") PORT_BIT( 0x9f, IP_ACTIVE_LOW, IPT_UNUSED) PORT_DIPNAME( 0x20, 0x00, "Speech" ) PORT_DIPSETTING( 0x20, "Off" ) PORT_DIPSETTING( 0x00, "On" ) PORT_DIPNAME( 0x40, 0x40, "Sounds" ) PORT_DIPSETTING( 0x00, "Tones" ) PORT_DIPSETTING( 0x40, "Synth" ) PORT_BIT( 0x100, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Audio Diag") PORT_CODE(KEYCODE_9_PAD) PORT_CHANGED_MEMBER(DEVICE_SELF, williams_s6_sound_device, audio_nmi, 1) INPUT_PORTS_END INPUT_CHANGED_MEMBER( williams_s6_sound_device::audio_nmi ) { // Diagnostic button sends a pulse to NMI pin if (newval==CLEAR_LINE) m_cpu->pulse_input_line(INPUT_LINE_NMI, attotime::zero); } //------------------------------------------------- // device_input_ports - return a pointer to // the device's I/O ports //------------------------------------------------- ioport_constructor williams_s6_sound_device::device_input_ports() const { return INPUT_PORTS_NAME( williams_s6 ); } //************************************************************************** // S9 SOUND BOARD (s6 with different interface, used in system 9 pinballs) //************************************************************************** //------------------------------------------------- // williams_s9_sound_device - constructor //------------------------------------------------- williams_s9_sound_device::williams_s9_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) : device_t(mconfig, WILLIAMS_S9_SOUND, tag, owner, clock) , device_mixer_interface(mconfig, *this) , m_cpu(*this, "cpu") , m_pia(*this, "pia") , m_hc(*this, "hc") { } //------------------------------------------------- // write - handle an external write //------------------------------------------------- void williams_s9_sound_device::write(u8 data) { m_pia->porta_w(data); } //------------------------------------------------- // strobe - tell PIA to process the input //------------------------------------------------- WRITE_LINE_MEMBER(williams_s9_sound_device::strobe) { m_pia->ca1_w(state); } //------------------------------------------------- // audio CPU map //------------------------------------------------- void williams_s9_sound_device::williams_s9_map(address_map &map) { map(0x0000, 0x07ff).ram(); map(0x2000, 0x2003).rw(m_pia, FUNC(pia6821_device::read), FUNC(pia6821_device::write)); map(0x8000, 0xffff).rom().region("audiocpu", 0 ); } //------------------------------------------------- // device_add_mconfig - add device configuration //------------------------------------------------- void williams_s9_sound_device::device_add_mconfig(machine_config &config) { M6802(config, m_cpu, XTAL(4'000'000)); m_cpu->set_addrmap(AS_PROGRAM, &williams_s9_sound_device::williams_s9_map); MC1408(config, "dac", 0).add_route(ALL_OUTPUTS, *this, 0.5); HC55516(config, m_hc, 0).add_route(ALL_OUTPUTS, *this, 1.00); PIA6821(config, m_pia, 0); m_pia->set_port_a_input_overrides_output_mask(0xff); m_pia->writepb_handler().set("dac", FUNC(dac_byte_interface::data_w)); m_pia->ca2_handler().set(m_hc, FUNC(hc55516_device::clock_w)); m_pia->cb2_handler().set(m_hc, FUNC(hc55516_device::digit_w)); m_pia->irqa_handler().set_inputline(m_cpu, M6802_IRQ_LINE); m_pia->irqb_handler().set_inputline(m_cpu, M6802_IRQ_LINE); } //------------------------------------------------- // device_start - device-specific startup //------------------------------------------------- void williams_s9_sound_device::device_start() { // register for save states save_item(NAME(m_dummy)); } //------------------------------------------------- // device_reset - device-specific reset //------------------------------------------------- void williams_s9_sound_device::device_reset() { // reset interrupt states m_cpu->set_input_line(M6802_IRQ_LINE, CLEAR_LINE); } INPUT_PORTS_START( williams_s9 ) PORT_START("S9") PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Audio Diag") PORT_CODE(KEYCODE_9_PAD) PORT_CHANGED_MEMBER(DEVICE_SELF, williams_s9_sound_device, audio_nmi, 1) INPUT_PORTS_END INPUT_CHANGED_MEMBER( williams_s9_sound_device::audio_nmi ) { // Diagnostic button sends a pulse to NMI pin if (newval==CLEAR_LINE) m_cpu->pulse_input_line(INPUT_LINE_NMI, attotime::zero); } //------------------------------------------------- // device_input_ports - return a pointer to // the device's I/O ports //------------------------------------------------- ioport_constructor williams_s9_sound_device::device_input_ports() const { return INPUT_PORTS_NAME( williams_s9 ); } //************************************************************************** // S11 SOUND BOARD (s9 with banked roms, used in system 11/a/b/c pinballs) //************************************************************************** //------------------------------------------------- // williams_s11_sound_device - constructor //------------------------------------------------- williams_s11_sound_device::williams_s11_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) : device_t(mconfig, WILLIAMS_S11_SOUND, tag, owner, clock) , device_mixer_interface(mconfig, *this) , m_cpu(*this, "cpu") , m_pia(*this, "pia") , m_hc(*this, "hc") { } //------------------------------------------------- // write - handle an external write //------------------------------------------------- void williams_s11_sound_device::write(u8 data) { m_pia->porta_w(data); } //------------------------------------------------- // strobe - tell PIA to process the input //------------------------------------------------- WRITE_LINE_MEMBER(williams_s11_sound_device::strobe) { m_pia->ca1_w(state); } //------------------------------------------------- // bank_w - bankswitch //------------------------------------------------- void williams_s11_sound_device::bank_w(u8 data) { membank("bank0")->set_entry(BIT(data, 1)); membank("bank1")->set_entry(BIT(data, 0)); } //------------------------------------------------- // audio CPU map //------------------------------------------------- void williams_s11_sound_device::williams_s11_map(address_map &map) { map(0x0000, 0x07ff).mirror(0x0800).ram(); map(0x1000, 0x1fff).w(FUNC(williams_s11_sound_device::bank_w)); map(0x2000, 0x2003).mirror(0x0ffc).rw(m_pia, FUNC(pia6821_device::read), FUNC(pia6821_device::write)); map(0x8000, 0xbfff).bankr("bank0"); map(0xc000, 0xffff).bankr("bank1"); } //------------------------------------------------- // device_add_mconfig - add device configuration //------------------------------------------------- void williams_s11_sound_device::device_add_mconfig(machine_config &config) { M6802(config, m_cpu, XTAL(4'000'000)); m_cpu->set_addrmap(AS_PROGRAM, &williams_s11_sound_device::williams_s11_map); MC1408(config, "dac", 0).add_route(ALL_OUTPUTS, *this, 0.5); HC55516(config, m_hc, 0).add_route(ALL_OUTPUTS, *this, 1.00); PIA6821(config, m_pia, 0); m_pia->set_port_a_input_overrides_output_mask(0xff); m_pia->writepb_handler().set("dac", FUNC(dac_byte_interface::data_w)); m_pia->ca2_handler().set(m_hc, FUNC(hc55516_device::clock_w)); m_pia->cb2_handler().set(m_hc, FUNC(hc55516_device::digit_w)); m_pia->irqa_handler().set_inputline(m_cpu, M6802_IRQ_LINE); m_pia->irqb_handler().set_inputline(m_cpu, M6802_IRQ_LINE); } //------------------------------------------------- // device_start - device-specific startup //------------------------------------------------- void williams_s11_sound_device::device_start() { u8 *ROM = memregion("audiocpu")->base(); membank("bank0")->configure_entries(0, 2, &ROM[0x0000], 0x4000); membank("bank1")->configure_entries(0, 2, &ROM[0x8000], 0x4000); // register for save states save_item(NAME(m_dummy)); } //------------------------------------------------- // device_reset - device-specific reset //------------------------------------------------- void williams_s11_sound_device::device_reset() { membank("bank0")->set_entry(0); membank("bank1")->set_entry(0); // reset interrupt states m_cpu->set_input_line(M6802_IRQ_LINE, CLEAR_LINE); } INPUT_PORTS_START( williams_s11 ) PORT_START("S11") PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Audio Diag") PORT_CODE(KEYCODE_9_PAD) PORT_CHANGED_MEMBER(DEVICE_SELF, williams_s11_sound_device, audio_nmi, 1) INPUT_PORTS_END INPUT_CHANGED_MEMBER( williams_s11_sound_device::audio_nmi ) { // Diagnostic button sends a pulse to NMI pin if (newval==CLEAR_LINE) m_cpu->pulse_input_line(INPUT_LINE_NMI, attotime::zero); } //------------------------------------------------- // device_input_ports - return a pointer to // the device's I/O ports //------------------------------------------------- ioport_constructor williams_s11_sound_device::device_input_ports() const { return INPUT_PORTS_NAME( williams_s11 ); }