summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/bus/msx_cart/msx_audio.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/bus/msx_cart/msx_audio.c')
-rw-r--r--src/devices/bus/msx_cart/msx_audio.c389
1 files changed, 389 insertions, 0 deletions
diff --git a/src/devices/bus/msx_cart/msx_audio.c b/src/devices/bus/msx_cart/msx_audio.c
new file mode 100644
index 00000000000..640dbc02b07
--- /dev/null
+++ b/src/devices/bus/msx_cart/msx_audio.c
@@ -0,0 +1,389 @@
+// license:BSD-3-Clause
+// copyright-holders:Wilbert Pol
+/**********************************************************************************
+
+Emulation of the different MSX-AUDIO devices:
+
+- Panasonic FS-CA1
+ - Y8950
+ - 4KB ram
+ - Connector for Toshiba HX-MU901 keyboard
+
+- Toshiba HX-MU900
+ - Y8950
+ - No midi ports
+ - No ram
+ - Connector for Toshiba HX-MU901 keyboard
+
+- Philips NMS-1205
+ - Y8950
+ - Midi ports
+ - 32KB sample ram
+ - Connector for Philips NMS-1160 keyboard
+
+
+The keyboards:
+- Toshiba HX-MU901
+ - 49 keys: 4 full octaves + high C
+ - ENTER key
+ - SELECT key
+ - multi sensor (?)
+
+- Philips NMS-1160
+ - 61 keys: 5 full octaves + high C
+ - Different wiring, so incompatible with the other keyboards
+
+- Panasonic YK-20
+ - 49 keys: 4 full octaves + high C
+
+
+TODO:
+- Test MIDI in/out/through
+- Sample RAM, doesn't seem to get written to
+- Test NMS-1160 keyboard
+- HX-MU901: ENTER/SELECT keys and multi sensors
+- NMS1160: Test the keyboard
+- NMS1205: Add DAC
+- NMS1205/FSCA1: Add muting of dac and y8950 based on io config writes.
+
+
+For testing the sample ram (playback of a cuckoo sound, the volume of the sound is quite low):
+- Disable firmware on the fs-ca1
+- Execute the following basic commands:
+ - CALL AUDIO
+ - CALL COPY PCM(#1,0)
+ - CALL PLAY PCM(0)
+
+See also http://www.mccm.hetlab.tk/millennium/milc/gc/topic_26.htm (dutch)
+and/or http://ngs.no.coocan.jp/doc/wiki.cgi/datapack?page=4.2+MSX-AUDIO+%B3%C8%C4%A5BASIC (japanese)
+
+
+**********************************************************************************/
+
+#include "emu.h"
+#include "msx_audio.h"
+#include "bus/msx_cart/msx_audio_kb.h"
+
+
+const device_type MSX_CART_MSX_AUDIO_HXMU900 = &device_creator<msx_cart_msx_audio_hxmu900>;
+const device_type MSX_CART_MSX_AUDIO_NMS1205 = &device_creator<msx_cart_msx_audio_nms1205>;
+const device_type MSX_CART_MSX_AUDIO_FSCA1 = &device_creator<msx_cart_msx_audio_fsca1>;
+
+
+msx_cart_msx_audio_hxmu900::msx_cart_msx_audio_hxmu900(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, MSX_CART_MSX_AUDIO_HXMU900, "MSX Cartridge - MSX-AUDIO HX-MU900", tag, owner, clock, "msx_audio_hxmu900", __FILE__)
+ , msx_cart_interface(mconfig, *this)
+ , m_y8950(*this, "y8950")
+{
+}
+
+
+static MACHINE_CONFIG_FRAGMENT( msx_audio_hxmu900 )
+ // This is actually incorrect. The sound output is passed back into the MSX machine where it is mixed internally and output through the system 'speaker'.
+ MCFG_SPEAKER_STANDARD_MONO("mono")
+ MCFG_SOUND_ADD("y8950", Y8950, XTAL_3_579545MHz) // Not verified
+ MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.40)
+ MCFG_Y8950_KEYBOARD_WRITE_HANDLER(DEVWRITE8("kbdc", msx_audio_kbdc_port_device, write))
+ MCFG_Y8950_KEYBOARD_READ_HANDLER(DEVREAD8("kbdc", msx_audio_kbdc_port_device, read))
+
+ MCFG_MSX_AUDIO_KBDC_PORT_ADD("kbdc", msx_audio_keyboards, NULL)
+MACHINE_CONFIG_END
+
+
+machine_config_constructor msx_cart_msx_audio_hxmu900::device_mconfig_additions() const
+{
+ return MACHINE_CONFIG_NAME( msx_audio_hxmu900 );
+}
+
+
+void msx_cart_msx_audio_hxmu900::device_start()
+{
+ // Install IO read/write handlers
+ address_space &space = machine().device<cpu_device>("maincpu")->space(AS_IO);
+ space.install_write_handler(0xc0, 0xc1, write8_delegate(FUNC(y8950_device::write), m_y8950.target()));
+ space.install_read_handler(0xc0, 0xc1, read8_delegate(FUNC(y8950_device::read), m_y8950.target()));
+}
+
+
+void msx_cart_msx_audio_hxmu900::initialize_cartridge()
+{
+ if (get_rom_size() < 0x8000)
+ {
+ fatalerror("msx_audio: Invalid ROM size\n");
+ }
+}
+
+
+READ8_MEMBER(msx_cart_msx_audio_hxmu900::read_cart)
+{
+ if (offset >= 0x4000 && offset < 0xC000)
+ {
+ return m_rom[offset - 0x4000];
+ }
+ return 0xff;
+}
+
+
+
+
+
+msx_cart_msx_audio_nms1205::msx_cart_msx_audio_nms1205(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, MSX_CART_MSX_AUDIO_NMS1205, "MSX Cartridge - MSX-AUDIO NMS-1205", tag, owner, clock, "msx_audio_nms1205", __FILE__)
+ , msx_cart_interface(mconfig, *this)
+ , m_y8950(*this, "y8950")
+ , m_acia6850(*this, "acia6850")
+ , m_mdout(*this, "mdout")
+ , m_mdthru(*this, "mdthru")
+{
+}
+
+
+static MACHINE_CONFIG_FRAGMENT( msx_audio_nms1205 )
+ // This is actually incorrect. The sound output is passed back into the MSX machine where it is mixed internally and output through the system 'speaker'.
+ // At the same time the sound is also output on two output on the nms1205 cartridge itself
+ MCFG_SPEAKER_STANDARD_MONO("mono")
+ MCFG_SOUND_ADD("y8950", Y8950, XTAL_3_579545MHz)
+ MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.40)
+ MCFG_Y8950_KEYBOARD_WRITE_HANDLER(DEVWRITE8("kbdc", msx_audio_kbdc_port_device, write))
+ MCFG_Y8950_KEYBOARD_READ_HANDLER(DEVREAD8("kbdc", msx_audio_kbdc_port_device, read))
+ MCFG_Y8950_IRQ_HANDLER(WRITELINE(msx_cart_msx_audio_nms1205, irq_write))
+
+ MCFG_MSX_AUDIO_KBDC_PORT_ADD("kbdc", msx_audio_keyboards, NULL)
+
+ // There is a 2 MHz crystal on the PCB, the 6850 TX and RX clocks are derived from it
+ MCFG_DEVICE_ADD("acia6850", ACIA6850, 0)
+ MCFG_ACIA6850_TXD_HANDLER(DEVWRITELINE("mdout", midi_port_device, write_txd))
+
+ MCFG_MIDI_PORT_ADD("mdin", midiin_slot, "midiin")
+ MCFG_MIDI_RX_HANDLER(WRITELINE(msx_cart_msx_audio_nms1205, midi_in))
+
+ MCFG_MIDI_PORT_ADD("mdthru", midiout_slot, "midiout")
+
+ MCFG_MIDI_PORT_ADD("mdout", midiout_slot, "midiout")
+MACHINE_CONFIG_END
+
+
+machine_config_constructor msx_cart_msx_audio_nms1205::device_mconfig_additions() const
+{
+ return MACHINE_CONFIG_NAME( msx_audio_nms1205 );
+}
+
+
+ROM_START( msx_nms1205 )
+ ROM_REGION(0x8000, "y8950", ROMREGION_ERASE00)
+ROM_END
+
+
+const rom_entry *msx_cart_msx_audio_nms1205::device_rom_region() const
+{
+ return ROM_NAME( msx_nms1205 );
+}
+
+
+WRITE_LINE_MEMBER(msx_cart_msx_audio_nms1205::irq_write)
+{
+ // Trigger IRQ on the maincpu
+ // The 8950 seems to trigger an irq on reset, this causes an infinite loop of continuously triggering
+ // the MSX's interrupt handler. The 8950 irq will never be cleared the nms1205's irq handler hook hasn't
+ // been installed yet.
+// m_out_irq_cb(state);
+}
+
+
+WRITE_LINE_MEMBER(msx_cart_msx_audio_nms1205::midi_in)
+{
+ // MIDI in signals is sent to both the 6850 and the MIDI thru output port
+ m_acia6850->write_rxd(state);
+ m_mdthru->write_txd(state);
+}
+
+
+void msx_cart_msx_audio_nms1205::device_start()
+{
+ // Install IO read/write handlers
+ address_space &space = machine().device<cpu_device>("maincpu")->space(AS_IO);
+ space.install_write_handler(0xc0, 0xc1, write8_delegate(FUNC(y8950_device::write), m_y8950.target()));
+ space.install_read_handler(0xc0, 0xc1, read8_delegate(FUNC(y8950_device::read), m_y8950.target()));
+ space.install_write_handler(0x00, 0x00, write8_delegate(FUNC(acia6850_device::control_w), m_acia6850.target()));
+ space.install_write_handler(0x01, 0x01, write8_delegate(FUNC(acia6850_device::data_w), m_acia6850.target()));
+ space.install_read_handler(0x04,0x04, read8_delegate(FUNC(acia6850_device::status_r), m_acia6850.target()));
+ space.install_read_handler(0x05,0x05, read8_delegate(FUNC(acia6850_device::data_r), m_acia6850.target()));
+}
+
+
+void msx_cart_msx_audio_nms1205::initialize_cartridge()
+{
+ if (get_rom_size() < 0x8000)
+ {
+ fatalerror("msx_audio: Invalid ROM size\n");
+ }
+}
+
+
+READ8_MEMBER(msx_cart_msx_audio_nms1205::read_cart)
+{
+ if (offset >= 0x4000 && offset < 0xC000)
+ {
+ return m_rom[offset - 0x4000];
+ }
+ return 0xff;
+}
+
+
+
+
+
+
+
+msx_cart_msx_audio_fsca1::msx_cart_msx_audio_fsca1(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, MSX_CART_MSX_AUDIO_FSCA1, "MSX Cartridge - MSX-AUDIO FS-CA1", tag, owner, clock, "msx_audio_fsca1", __FILE__)
+ , msx_cart_interface(mconfig, *this)
+ , m_y8950(*this, "y8950")
+ , m_io_config(*this, "CONFIG")
+ , m_region_y8950(*this, "y8950")
+ , m_7ffe(0)
+ , m_7fff(0)
+{
+}
+
+
+static MACHINE_CONFIG_FRAGMENT( msx_audio_fsca1 )
+ // This is actually incorrect. The sound output is passed back into the MSX machine where it is mixed internally and output through the system 'speaker'.
+ MCFG_SPEAKER_STANDARD_MONO("mono")
+ MCFG_SOUND_ADD("y8950", Y8950, XTAL_3_579545MHz)
+ MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.40)
+ MCFG_Y8950_KEYBOARD_WRITE_HANDLER(DEVWRITE8("kbdc", msx_audio_kbdc_port_device, write))
+ MCFG_Y8950_KEYBOARD_READ_HANDLER(DEVREAD8("kbdc", msx_audio_kbdc_port_device, read))
+ MCFG_Y8950_IO_READ_HANDLER(READ8(msx_cart_msx_audio_fsca1, y8950_io_r))
+ MCFG_Y8950_IO_WRITE_HANDLER(WRITE8(msx_cart_msx_audio_fsca1, y8950_io_w))
+
+ MCFG_MSX_AUDIO_KBDC_PORT_ADD("kbdc", msx_audio_keyboards, NULL)
+MACHINE_CONFIG_END
+
+
+machine_config_constructor msx_cart_msx_audio_fsca1::device_mconfig_additions() const
+{
+ return MACHINE_CONFIG_NAME( msx_audio_fsca1 );
+}
+
+
+static INPUT_PORTS_START( msx_audio_fsca1 )
+ PORT_START("CONFIG")
+ PORT_CONFNAME( 0x04, 0x04, "FS-CA1 Firmware switch")
+ PORT_CONFSETTING( 0x04, "On" )
+ PORT_CONFSETTING( 0x00, "Off" )
+ PORT_BIT(0xFB, IP_ACTIVE_HIGH, IPT_UNKNOWN)
+INPUT_PORTS_END
+
+
+ioport_constructor msx_cart_msx_audio_fsca1::device_input_ports() const
+{
+ return INPUT_PORTS_NAME( msx_audio_fsca1 );
+}
+
+
+ROM_START( msx_fsca1 )
+ ROM_REGION(0x8000, "y8950", ROMREGION_ERASE00)
+ROM_END
+
+
+const rom_entry *msx_cart_msx_audio_fsca1::device_rom_region() const
+{
+ return ROM_NAME( msx_fsca1 );
+}
+
+
+void msx_cart_msx_audio_fsca1::device_start()
+{
+ // Install IO read/write handlers
+ address_space &space = machine().device<cpu_device>("maincpu")->space(AS_IO);
+ space.install_write_handler(0xc0, 0xc3, write8_delegate(FUNC(msx_cart_msx_audio_fsca1::write_y8950), this));
+ space.install_read_handler(0xc0, 0xc3, read8_delegate(FUNC(msx_cart_msx_audio_fsca1::read_y8950), this));
+}
+
+
+void msx_cart_msx_audio_fsca1::initialize_cartridge()
+{
+ if (get_rom_size() < 0x20000)
+ {
+ fatalerror("msx_audio_fsca1: Invalid ROM size\n");
+ }
+}
+
+
+READ8_MEMBER(msx_cart_msx_audio_fsca1::read_cart)
+{
+ if (m_7ffe == 0 && (offset & 0xB000) == 0x3000)
+ {
+ return m_sram[offset & 0xfff];
+ }
+ return m_rom[((m_7ffe & 0x03) << 15) | (offset & 0x7fff)];
+}
+
+
+WRITE8_MEMBER(msx_cart_msx_audio_fsca1::write_cart)
+{
+ if (offset == 0x7ffe)
+ {
+ m_7ffe = data;
+ return;
+ }
+
+ if (offset == 0x7fff)
+ {
+ m_7fff = data;
+ return;
+ }
+
+ if (m_7ffe == 0 && (offset & 0xb000) == 0x3000)
+ {
+ m_sram[offset & 0xfff] = data;
+ return;
+ }
+
+ logerror("msx_cart_msx_audio_fsca1: Unhandled write %02x to %04x\n", data, offset);
+}
+
+
+WRITE8_MEMBER(msx_cart_msx_audio_fsca1::write_y8950)
+{
+ if (offset & 2)
+ {
+ if (m_7fff & 0x02)
+ {
+ m_y8950->write(space, offset, data);
+ }
+ }
+ else
+ {
+ if (m_7fff & 0x01)
+ {
+ m_y8950->write(space, offset, data);
+ }
+ }
+}
+
+
+READ8_MEMBER(msx_cart_msx_audio_fsca1::read_y8950)
+{
+ if (offset & 2)
+ {
+ return (m_7fff & 0x02) ? m_y8950->read(space, offset) : 0xff;
+ }
+ else
+ {
+ return (m_7fff & 0x01) ? m_y8950->read(space, offset) : 0xff;
+ }
+}
+
+
+WRITE8_MEMBER(msx_cart_msx_audio_fsca1::y8950_io_w)
+{
+ logerror("msx_fsca1::y8950_io_w: %02x\n", data);
+}
+
+
+READ8_MEMBER(msx_cart_msx_audio_fsca1::y8950_io_r)
+{
+ return m_io_config->read();
+}