mirror of
https://github.com/holub/mame
synced 2025-06-02 19:06:43 +03:00
513 lines
12 KiB
C++
513 lines
12 KiB
C++
// license:BSD-3-Clause
|
|
// copyright-holders:R. Belmont
|
|
/*********************************************************************
|
|
|
|
a2mockingboard.c
|
|
|
|
Implementation of the Sweet Micro Systems Mockingboard card
|
|
and friends.
|
|
|
|
*********************************************************************/
|
|
|
|
#include "emu.h"
|
|
#include "a2mockingboard.h"
|
|
#include "speaker.h"
|
|
|
|
|
|
/***************************************************************************
|
|
PARAMETERS
|
|
***************************************************************************/
|
|
|
|
#define VIA1_TAG "mockbd_via1"
|
|
#define VIA2_TAG "mockbd_via2"
|
|
#define AY1_TAG "mockbd_ay1"
|
|
#define AY2_TAG "mockbd_ay2"
|
|
#define AY3_TAG "mockbd_ay3"
|
|
#define AY4_TAG "mockbd_ay4"
|
|
#define E2P_TMS_TAG "tms5220"
|
|
|
|
//**************************************************************************
|
|
// GLOBAL VARIABLES
|
|
//**************************************************************************
|
|
|
|
DEFINE_DEVICE_TYPE(A2BUS_MOCKINGBOARD, a2bus_mockingboard_device, "a2mockbd", "Sweet Micro Systems Mockingboard Sound/Speech I")
|
|
DEFINE_DEVICE_TYPE(A2BUS_PHASOR, a2bus_phasor_device, "a2phasor", "Applied Engineering Phasor")
|
|
DEFINE_DEVICE_TYPE(A2BUS_ECHOPLUS, a2bus_echoplus_device, "a2echop", "Street Electronics Echo Plus")
|
|
|
|
//-------------------------------------------------
|
|
// device_add_mconfig - add device configuration
|
|
//-------------------------------------------------
|
|
|
|
void a2bus_ayboard_device::add_common_devices(machine_config &config)
|
|
{
|
|
VIA6522(config, m_via1, 1022727);
|
|
m_via1->writepa_handler().set(FUNC(a2bus_ayboard_device::via1_out_a));
|
|
m_via1->writepb_handler().set(FUNC(a2bus_ayboard_device::via1_out_b));
|
|
m_via1->irq_handler().set(FUNC(a2bus_ayboard_device::via1_irq_w));
|
|
|
|
VIA6522(config, m_via2, 1022727);
|
|
m_via2->writepa_handler().set(FUNC(a2bus_ayboard_device::via2_out_a));
|
|
m_via2->writepb_handler().set(FUNC(a2bus_ayboard_device::via2_out_b));
|
|
m_via2->irq_handler().set(FUNC(a2bus_ayboard_device::via2_irq_w));
|
|
|
|
SPEAKER(config, "lspeaker").front_left();
|
|
SPEAKER(config, "rspeaker").front_right();
|
|
AY8913(config, m_ay1, 1022727);
|
|
m_ay1->add_route(ALL_OUTPUTS, "lspeaker", 0.5);
|
|
}
|
|
|
|
void a2bus_ayboard_device::device_add_mconfig(machine_config &config)
|
|
{
|
|
add_common_devices(config);
|
|
|
|
AY8913(config, m_ay2, 1022727);
|
|
m_ay2->add_route(ALL_OUTPUTS, "rspeaker", 0.5);
|
|
}
|
|
|
|
void a2bus_mockingboard_device::device_add_mconfig(machine_config &config)
|
|
{
|
|
add_common_devices(config);
|
|
|
|
m_via1->writepb_handler().set(FUNC(a2bus_mockingboard_device::via1_out_b));
|
|
m_via1->cb2_handler().set(FUNC(a2bus_mockingboard_device::write_via1_cb2));
|
|
|
|
AY8913(config, m_ay2, 1022727);
|
|
m_ay2->add_route(ALL_OUTPUTS, "rspeaker", 0.5);
|
|
|
|
VOTRAX_SC01(config, m_sc01, 1022727);
|
|
m_sc01->ar_callback().set(m_via1, FUNC(via6522_device::write_cb1));
|
|
m_sc01->add_route(ALL_OUTPUTS, "lspeaker", 1.0);
|
|
m_sc01->add_route(ALL_OUTPUTS, "rspeaker", 1.0);
|
|
}
|
|
|
|
void a2bus_phasor_device::device_add_mconfig(machine_config &config)
|
|
{
|
|
add_common_devices(config);
|
|
|
|
m_via1->writepb_handler().set(FUNC(a2bus_phasor_device::via1_out_b));
|
|
m_via2->writepb_handler().set(FUNC(a2bus_phasor_device::via2_out_b));
|
|
|
|
SPEAKER(config, "lspeaker2").front_left();
|
|
SPEAKER(config, "rspeaker2").front_right();
|
|
AY8913(config, m_ay2, 1022727);
|
|
AY8913(config, m_ay3, 1022727);
|
|
AY8913(config, m_ay4, 1022727);
|
|
m_ay2->add_route(ALL_OUTPUTS, "lspeaker2", 0.5);
|
|
m_ay3->add_route(ALL_OUTPUTS, "rspeaker", 0.5);
|
|
m_ay4->add_route(ALL_OUTPUTS, "rspeaker2", 0.5);
|
|
}
|
|
|
|
void a2bus_echoplus_device::device_add_mconfig(machine_config &config)
|
|
{
|
|
add_common_devices(config);
|
|
|
|
AY8913(config, m_ay2, 1022727);
|
|
m_ay2->add_route(ALL_OUTPUTS, "rspeaker", 0.5);
|
|
|
|
SPEAKER(config, "echosp").front_center();
|
|
TMS5220(config, m_tms, 640000);
|
|
// echo+ has a TSP5220C soldered down on it
|
|
m_tms->add_route(ALL_OUTPUTS, "echosp", 1.0);
|
|
}
|
|
|
|
//**************************************************************************
|
|
// LIVE DEVICE
|
|
//**************************************************************************
|
|
|
|
a2bus_ayboard_device::a2bus_ayboard_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) :
|
|
device_t(mconfig, type, tag, owner, clock),
|
|
device_a2bus_card_interface(mconfig, *this),
|
|
m_via1(*this, VIA1_TAG),
|
|
m_via2(*this, VIA2_TAG),
|
|
m_ay1(*this, AY1_TAG),
|
|
m_ay2(*this, AY2_TAG),
|
|
m_porta1(0),
|
|
m_porta2(0)
|
|
{
|
|
}
|
|
|
|
a2bus_mockingboard_device::a2bus_mockingboard_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
|
|
a2bus_ayboard_device(mconfig, A2BUS_MOCKINGBOARD, tag, owner, clock),
|
|
m_sc01(*this, "sc01")
|
|
{
|
|
}
|
|
|
|
a2bus_phasor_device::a2bus_phasor_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
|
|
a2bus_ayboard_device(mconfig, A2BUS_PHASOR, tag, owner, clock),
|
|
m_ay3(*this, AY3_TAG),
|
|
m_ay4(*this, AY4_TAG),
|
|
m_native(false)
|
|
{
|
|
}
|
|
|
|
a2bus_echoplus_device::a2bus_echoplus_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
|
|
a2bus_ayboard_device(mconfig, A2BUS_ECHOPLUS, tag, owner, clock),
|
|
m_tms(*this, E2P_TMS_TAG)
|
|
{
|
|
}
|
|
|
|
//-------------------------------------------------
|
|
// device_start - device-specific startup
|
|
//-------------------------------------------------
|
|
|
|
void a2bus_ayboard_device::device_start()
|
|
{
|
|
save_item(NAME(m_porta1));
|
|
save_item(NAME(m_porta2));
|
|
}
|
|
|
|
void a2bus_ayboard_device::device_reset()
|
|
{
|
|
m_porta1 = m_porta2 = 0;
|
|
}
|
|
|
|
/*-------------------------------------------------
|
|
read_cnxx - called for reads from this card's cnxx space
|
|
-------------------------------------------------*/
|
|
|
|
uint8_t a2bus_ayboard_device::read_cnxx(uint8_t offset)
|
|
{
|
|
if (offset <= 0x10)
|
|
return m_via1->read(offset & 0xf);
|
|
else if (offset >= 0x80 && offset <= 0x90)
|
|
return m_via2->read(offset & 0xf);
|
|
|
|
return 0;
|
|
}
|
|
|
|
uint8_t a2bus_phasor_device::read_cnxx(uint8_t offset)
|
|
{
|
|
uint8_t ret = 0;
|
|
int via_sel;
|
|
|
|
if (m_native)
|
|
via_sel = ((offset & 0x80) >> 6) | ((offset & 0x10) >> 4);
|
|
else
|
|
via_sel = (offset & 0x80) ? 2 : 1;
|
|
|
|
if ((offset <= 0x20) || (offset >= 0x80 && offset <= 0xa0))
|
|
{
|
|
if (BIT(via_sel, 0))
|
|
ret |= m_via1->read(offset & 0xf);
|
|
|
|
if (BIT(via_sel, 1))
|
|
ret |= m_via2->read(offset & 0xf);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*-------------------------------------------------
|
|
write_cnxx - called for writes to this card's c0nx space
|
|
-------------------------------------------------*/
|
|
|
|
void a2bus_ayboard_device::write_cnxx(uint8_t offset, uint8_t data)
|
|
{
|
|
if (offset <= 0x10)
|
|
m_via1->write(offset & 0xf, data);
|
|
else if (offset >= 0x80 && offset <= 0x90)
|
|
m_via2->write(offset & 0xf, data);
|
|
else
|
|
logerror("Mockingboard(%d): unk write %02x to Cn%02X (%s)\n", slotno(), data, offset, machine().describe_context());
|
|
}
|
|
|
|
void a2bus_phasor_device::write_cnxx(uint8_t offset, uint8_t data)
|
|
{
|
|
if ((offset <= 0x20) || (offset >= 0x80 && offset <= 0xa0))
|
|
{
|
|
int via_sel;
|
|
|
|
if (m_native)
|
|
via_sel = ((offset & 0x80) >> 6) | ((offset & 0x10) >> 4);
|
|
else
|
|
via_sel = (offset & 0x80) ? 2 : 1;
|
|
|
|
if (BIT(via_sel, 0))
|
|
m_via1->write(offset & 0xf, data);
|
|
|
|
if (BIT(via_sel, 1))
|
|
m_via2->write(offset & 0xf, data);
|
|
}
|
|
}
|
|
|
|
WRITE_LINE_MEMBER( a2bus_ayboard_device::via1_irq_w )
|
|
{
|
|
if (state)
|
|
raise_slot_irq();
|
|
else
|
|
lower_slot_irq();
|
|
}
|
|
|
|
WRITE_LINE_MEMBER( a2bus_ayboard_device::via2_irq_w )
|
|
{
|
|
if (state)
|
|
raise_slot_irq();
|
|
else
|
|
lower_slot_irq();
|
|
}
|
|
|
|
void a2bus_ayboard_device::via1_out_a(uint8_t data)
|
|
{
|
|
m_porta1 = data;
|
|
}
|
|
|
|
void a2bus_ayboard_device::via1_out_b(uint8_t data)
|
|
{
|
|
if (!BIT(data, 2))
|
|
{
|
|
m_ay1->reset_w();
|
|
}
|
|
else
|
|
{
|
|
switch (data & 3)
|
|
{
|
|
case 0: // BDIR=0, BC1=0 (inactive)
|
|
break;
|
|
|
|
case 1: // BDIR=0, BC1=1 (read PSG)
|
|
m_porta1 = m_ay1->data_r();
|
|
break;
|
|
|
|
case 2: // BDIR=1, BC1=0 (write PSG)
|
|
m_ay1->data_w(m_porta1);
|
|
break;
|
|
|
|
case 3: // BDIR=1, BC1=1 (latch)
|
|
m_ay1->address_w(m_porta1);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void a2bus_phasor_device::via1_out_b(uint8_t data)
|
|
{
|
|
if (!(data & 4))
|
|
{
|
|
m_ay1->reset_w();
|
|
if (m_native)
|
|
{
|
|
m_ay2->reset_w();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int chip_sel;
|
|
|
|
if (m_native)
|
|
chip_sel = (~(data >> 3) & 3);
|
|
else
|
|
chip_sel = 1;
|
|
|
|
// logerror("Phasor: %02x to AY1/2 CS %02x (BDIR/BC1 %02x, data %02x)\n", m_porta1, chipSel, data & 3, data);
|
|
switch (data & 3)
|
|
{
|
|
case 0: // BDIR=0, BC1=0 (inactive)
|
|
break;
|
|
|
|
case 1: // BDIR=0, BC1=1 (read PSG)
|
|
if (BIT(chip_sel, 0))
|
|
m_porta1 = m_ay1->data_r();
|
|
if (BIT(chip_sel, 1))
|
|
m_porta1 = m_ay2->data_r();
|
|
break;
|
|
|
|
case 2: // BDIR=1, BC1=0 (write PSG)
|
|
if (BIT(chip_sel, 0))
|
|
m_ay1->data_w(m_porta1);
|
|
if (BIT(chip_sel, 1))
|
|
m_ay2->data_w(m_porta1);
|
|
break;
|
|
|
|
case 3: // BDIR=1, BC1=1 (latch)
|
|
if (BIT(chip_sel, 0))
|
|
m_ay1->address_w(m_porta1);
|
|
if (BIT(chip_sel, 1))
|
|
m_ay2->address_w(m_porta1);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void a2bus_ayboard_device::via2_out_a(uint8_t data)
|
|
{
|
|
m_porta2 = data;
|
|
}
|
|
|
|
void a2bus_ayboard_device::via2_out_b(uint8_t data)
|
|
{
|
|
if (!BIT(data, 2))
|
|
{
|
|
m_ay2->reset_w();
|
|
}
|
|
else
|
|
{
|
|
switch (data & 3)
|
|
{
|
|
case 0: // BDIR=0, BC1=0 (inactive)
|
|
break;
|
|
|
|
case 1: // BDIR=0, BC1=1 (read PSG)
|
|
m_porta2 = m_ay2->data_r();
|
|
break;
|
|
|
|
case 2: // BDIR=1, BC1=0 (write PSG)
|
|
m_ay2->data_w(m_porta2);
|
|
break;
|
|
|
|
case 3: // BDIR=1, BC1=1 (latch)
|
|
m_ay2->address_w(m_porta2);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void a2bus_phasor_device::via2_out_b(uint8_t data)
|
|
{
|
|
if (!BIT(data, 2))
|
|
{
|
|
if (m_native)
|
|
{
|
|
m_ay3->reset_w();
|
|
m_ay4->reset_w();
|
|
}
|
|
else
|
|
{
|
|
m_ay2->reset_w();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int chip_sel;
|
|
|
|
if (m_native)
|
|
chip_sel = (~(data >> 3) & 3);
|
|
else
|
|
chip_sel = 1;
|
|
|
|
// logerror("Phasor: %02x to AY3/4 CS %02x (BDIR/BC1 %02x, data %02x)\n", m_porta2, chipSel, data & 3, data);
|
|
switch (data & 3)
|
|
{
|
|
case 0: // BDIR=0, BC1=0 (inactive)
|
|
break;
|
|
|
|
case 1: // BDIR=0, BC1=1 (read PSG)
|
|
if (BIT(chip_sel, 0))
|
|
m_porta2 = m_ay3->data_r();
|
|
if (BIT(chip_sel, 1))
|
|
m_porta2 = m_ay4->data_r();
|
|
break;
|
|
|
|
case 2: // BDIR=1, BC1=0 (write PSG)
|
|
if (BIT(chip_sel, 0))
|
|
m_ay3->data_w(m_porta2);
|
|
if (BIT(chip_sel, 1))
|
|
m_ay4->data_w(m_porta2);
|
|
break;
|
|
|
|
case 3: // BDIR=1, BC1=1 (latch)
|
|
if (BIT(chip_sel, 0))
|
|
m_ay3->address_w(m_porta2);
|
|
if (BIT(chip_sel, 1))
|
|
m_ay4->address_w(m_porta2);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void a2bus_phasor_device::set_clocks()
|
|
{
|
|
if (m_native)
|
|
{
|
|
m_ay1->set_clock(1022727*2);
|
|
m_ay2->set_clock(1022727*2);
|
|
m_ay3->set_clock(1022727*2);
|
|
m_ay4->set_clock(1022727*2);
|
|
}
|
|
else
|
|
{
|
|
m_ay1->set_clock(1022727);
|
|
m_ay2->set_clock(1022727);
|
|
m_ay3->set_clock(1022727);
|
|
m_ay4->set_clock(1022727);
|
|
}
|
|
}
|
|
|
|
uint8_t a2bus_phasor_device::read_c0nx(uint8_t offset)
|
|
{
|
|
m_native = BIT(offset, 0);
|
|
set_clocks();
|
|
return 0xff;
|
|
}
|
|
|
|
void a2bus_phasor_device::write_c0nx(uint8_t offset, uint8_t data)
|
|
{
|
|
m_native = BIT(offset, 0);
|
|
set_clocks();
|
|
}
|
|
|
|
uint8_t a2bus_echoplus_device::read_c0nx(uint8_t offset)
|
|
{
|
|
switch (offset)
|
|
{
|
|
case 0:
|
|
return 0x1f | m_tms->status_r();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void a2bus_echoplus_device::write_c0nx(uint8_t offset, uint8_t data)
|
|
{
|
|
switch (offset)
|
|
{
|
|
case 0:
|
|
m_tms->data_w(data);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void a2bus_mockingboard_device::device_reset()
|
|
{
|
|
m_porta1 = m_porta2 = m_portb1 = 0;
|
|
m_last_cb2_state = ASSERT_LINE;
|
|
}
|
|
|
|
WRITE_LINE_MEMBER( a2bus_mockingboard_device::write_via1_cb2 )
|
|
{
|
|
if ((state == CLEAR_LINE) && (m_last_cb2_state == ASSERT_LINE))
|
|
{
|
|
m_sc01->write(m_portb1);
|
|
}
|
|
m_last_cb2_state = state;
|
|
}
|
|
|
|
void a2bus_mockingboard_device::via1_out_b(uint8_t data)
|
|
{
|
|
m_portb1 = data;
|
|
|
|
if (!BIT(data, 2))
|
|
{
|
|
m_ay1->reset_w();
|
|
}
|
|
else
|
|
{
|
|
switch (data & 3)
|
|
{
|
|
case 0: // BDIR=0, BC1=0 (inactive)
|
|
break;
|
|
|
|
case 1: // BDIR=0, BC1=1 (read PSG)
|
|
m_porta1 = m_ay1->data_r();
|
|
break;
|
|
|
|
case 2: // BDIR=1, BC1=0 (write PSG)
|
|
m_ay1->data_w(m_porta1);
|
|
break;
|
|
|
|
case 3: // BDIR=1, BC1=1 (latch)
|
|
m_ay1->address_w(m_porta1);
|
|
break;
|
|
}
|
|
}
|
|
}
|