summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/bus/cbus/amd98.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/bus/cbus/amd98.cpp')
-rw-r--r--src/devices/bus/cbus/amd98.cpp220
1 files changed, 220 insertions, 0 deletions
diff --git a/src/devices/bus/cbus/amd98.cpp b/src/devices/bus/cbus/amd98.cpp
new file mode 100644
index 00000000000..8edfecafe60
--- /dev/null
+++ b/src/devices/bus/cbus/amd98.cpp
@@ -0,0 +1,220 @@
+// license:BSD-3-Clause
+// copyright-holders:Angelo Salese
+/**************************************************************************************************
+
+System Sacom AMD-98 (AmuseMent boarD)
+
+3 PSG chips, one of the first sound boards released for PC98
+Superseded by later NEC in-house sound boards
+
+TODO:
+- not sure if it's AY8910 or YM2203, from a PCB pic it looks with stock AY logos?
+- f/f not completely understood;
+- PIT control;
+- PCM section;
+
+===================================================================================================
+
+- Known games with AMD-98 support
+ Brown's Run (System Sacom)
+ Dome (System Sacom)
+ Highway Star (System Sacom)
+ Marchen Veil I (System Sacom)
+ Marchen Veil II (System Sacom)
+ Zone (System Sacom)
+ Relics (Bothtec)
+ Thexder (Game Arts)
+
+**************************************************************************************************/
+
+#include "emu.h"
+#include "bus/cbus/amd98.h"
+#include "speaker.h"
+
+#define LOG_LATCH (1U << 1) // Detailed AY3 latch setups
+
+
+#define VERBOSE (LOG_GENERAL)
+//#define LOG_OUTPUT_FUNC osd_printf_info
+#include "logmacro.h"
+
+#define LOGLATCH(...) LOGMASKED(LOG_LATCH, __VA_ARGS__)
+
+
+//**************************************************************************
+// GLOBAL VARIABLES
+//**************************************************************************
+
+// device type definition
+DEFINE_DEVICE_TYPE(AMD98, amd98_device, "amd98", "System Sacom AMD-98")
+
+//-------------------------------------------------
+// device_add_mconfig - add device configuration
+//-------------------------------------------------
+
+void amd98_device::device_add_mconfig(machine_config &config)
+{
+ // Assume mono, as per highway making engine noise from ay1 only
+ SPEAKER(config, "speaker").front_center();
+ AY8910(config, m_ay1, 1'996'800);
+ m_ay1->port_a_read_callback().set_ioport("OPN_PA1");
+ m_ay1->port_b_write_callback().set(FUNC(amd98_device::ay3_address_w));
+ m_ay1->add_route(ALL_OUTPUTS, "speaker", 0.50);
+
+ AY8910(config, m_ay2, 1'996'800);
+ m_ay2->port_a_read_callback().set_ioport("OPN_PA2");
+ m_ay2->port_b_write_callback().set(FUNC(amd98_device::ay3_data_latch_w));
+ m_ay2->add_route(ALL_OUTPUTS, "speaker", 0.50);
+
+ AY8910(config, m_ay3, 1'996'800);
+ m_ay3->port_b_write_callback().set([this] (u8 data) {
+ LOG("AMD-98 DAC %02x\n", data);
+ });
+ m_ay3->add_route(ALL_OUTPUTS, "speaker", 0.25);
+}
+
+static INPUT_PORTS_START( pc9801_amd98 )
+ PORT_START("OPN_PA1")
+ PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_8WAY PORT_PLAYER(1) PORT_NAME("P1 Joystick Up")
+ PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_8WAY PORT_PLAYER(1) PORT_NAME("P1 Joystick Down")
+ PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_8WAY PORT_PLAYER(1) PORT_NAME("P1 Joystick Left")
+ PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_8WAY PORT_PLAYER(1) PORT_NAME("P1 Joystick Right")
+ PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(1) PORT_NAME("P1 Joystick Button 1")
+ PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(1) PORT_NAME("P1 Joystick Button 2")
+ PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
+ PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )
+
+ PORT_START("OPN_PA2")
+ PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_8WAY PORT_PLAYER(2) PORT_NAME("P2 Joystick Up")
+ PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_8WAY PORT_PLAYER(2) PORT_NAME("P2 Joystick Down")
+ PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_8WAY PORT_PLAYER(2) PORT_NAME("P2 Joystick Left")
+ PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_8WAY PORT_PLAYER(2) PORT_NAME("P2 Joystick Right")
+ PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(2) PORT_NAME("P2 Joystick Button 1")
+ PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(2) PORT_NAME("P2 Joystick Button 2")
+ PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED )
+ PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )
+INPUT_PORTS_END
+
+ioport_constructor amd98_device::device_input_ports() const
+{
+ return INPUT_PORTS_NAME( pc9801_amd98 );
+}
+
+
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// amd98_device - constructor
+//-------------------------------------------------
+
+amd98_device::amd98_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : device_t(mconfig, AMD98, tag, owner, clock)
+ , m_bus(*this, DEVICE_SELF_OWNER)
+ , m_ay1(*this, "ay1")
+ , m_ay2(*this, "ay2")
+ , m_ay3(*this, "ay3")
+{
+}
+
+
+//-------------------------------------------------
+// device_validity_check - perform validity checks
+// on this device
+//-------------------------------------------------
+
+void amd98_device::device_validity_check(validity_checker &valid) const
+{
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void amd98_device::device_start()
+{
+}
+
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void amd98_device::device_reset()
+{
+ m_bus->install_io(0x00d8, 0x00df, read8sm_delegate(*this, FUNC(amd98_device::read)), write8sm_delegate(*this, FUNC(amd98_device::write)));
+ // thexder access with following
+ m_bus->install_io(0x38d8, 0x38df, read8sm_delegate(*this, FUNC(amd98_device::read)), write8sm_delegate(*this, FUNC(amd98_device::write)));
+}
+
+
+//**************************************************************************
+// READ/WRITE HANDLERS
+//**************************************************************************
+
+uint8_t amd98_device::read(offs_t offset)
+{
+ switch(offset)
+ {
+ case 2:
+ return m_ay1->data_r();
+ case 3:
+ return m_ay2->data_r();
+ }
+
+ LOG("AMD-98: unhandled %02x read\n", offset + 0xd8);
+
+ return 0xff;
+}
+
+void amd98_device::write(offs_t offset, uint8_t data)
+{
+ switch(offset)
+ {
+ case 0:
+ m_ay1->address_w(data);
+ break;
+ case 1:
+ m_ay2->address_w(data);
+ break;
+ case 2:
+ m_ay1->data_w(data);
+ break;
+ case 3:
+ m_ay2->data_w(data);
+ break;
+ default:
+ LOG("AMD-98: unhandled %02x write %02x\n", offset + 0xd8, data);
+ }
+}
+
+void amd98_device::ay3_address_w(uint8_t data)
+{
+ LOGLATCH("AMD-98 AY3 latch %02x\n", data);
+ m_ay3_latch = data;
+}
+
+void amd98_device::ay3_data_latch_w(uint8_t data)
+{
+ // TODO: actually goes 0 -> 1 -> 0
+ // TODO: thexder is the odd one: uses 0x00 -> 0x40 -> 0x47 (address) -> 0x40 -> 0x40 -> 0x43 (data) -> 0x40
+ if (!BIT(m_ay3_ff, 0) && BIT(data, 0))
+ {
+ switch(data & 0xc2)
+ {
+ case 0x42:
+ LOG("AMD-98 AY3 write %02x address (f/f %02x)\n", m_ay3_latch, m_ay3_ff);
+ m_ay3->address_w(m_ay3_latch);
+ break;
+ case 0x40:
+ LOG("AMD-98 AY3 write %02x data (f/f %02x)\n", m_ay3_latch, m_ay3_ff);
+ m_ay3->data_w(m_ay3_latch);
+ break;
+ }
+ }
+ LOGLATCH("AMD-98 f/f %02x %02x\n", data, m_ay3_latch);
+ m_ay3_ff = data;
+}