summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu/machine/mpu401.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/emu/machine/mpu401.c')
-rw-r--r--src/emu/machine/mpu401.c287
1 files changed, 287 insertions, 0 deletions
diff --git a/src/emu/machine/mpu401.c b/src/emu/machine/mpu401.c
new file mode 100644
index 00000000000..7e654cb0662
--- /dev/null
+++ b/src/emu/machine/mpu401.c
@@ -0,0 +1,287 @@
+/***************************************************************************
+
+ Roland MPU-401 core
+
+ This emulates the MPU-401 external box with the 6801, ASIC, and RAM in it.
+
+ We do it this way to facilitate the various PC, Apple II, C64, and other
+ possible hookups.
+
+ 6801 GPIO port hookups (from the schematics)
+
+ P10 / P11 / P12: drive the metronome and speaker
+ P13 / P14 / P15: drive 3 pins on the SYNC OUT connector
+ P16: to DSRD on gate array
+ P17: to DRRD on gate array
+
+ P20: to SYC OUT on gate array
+ P21: to SYC IN on gate array, pulled up to Vcc via 4.7K resistor
+ programmed as output of timer (OLVL)
+ P22: to SRCK on gate array, inverted
+ P23: MIDI IN serial data (SCI in)
+ P24: MIDI OUT serial data (SCI out)
+
+ ASIC addresses from the 6801:
+ 0x20: (r) read pending byte from the PC (w) apparently nothing
+ 0x21: (r) ASIC status, see STAT_xxx bits below (w) send new byte to PC data port
+
+ Theory of operation: 6801's timer/counter is set up to drive a pulse stream
+ out P21 to the ASIC's SYC IN pin. The ASIC in turn generates the MIDI baud
+ rate (times 8) and returns that on pin P22.
+
+ The 6801 is believed to run in mode 2, based on a combination of the
+ schematics and the behavior (ie, internal RAM from 80-FF is clearly
+ present from the program's behavior, and ports 3/4 are obviously external
+ address/data buses)
+
+***************************************************************************/
+
+#include "machine/mpu401.h"
+#include "bus/midi/midi.h"
+
+#define M6801_TAG "mpu6801"
+#define ROM_TAG "mpurom"
+#define MIDIIN_TAG "mdin"
+#define MIDIOUT_TAG "mdout"
+
+#define P2_SYNC_OUT (0x01)
+#define P2_SYNC_IN (0x02)
+#define P2_SRCK_OUT (0x04)
+#define P2_MIDI_IN (0x08)
+#define P2_MIDI_OUT (0x10)
+
+#define STAT_CMD_PORT (0x01) // set if the new byte indicated by TX FULL was written to the command port, clear for data port
+#define STAT_TX_FULL (0x40) // indicates the PC has written a new byte we haven't read yet
+#define STAT_RX_EMPTY (0x80) // indicates we've written a new byte the PC hasn't read yet
+
+static ADDRESS_MAP_START( mpu401_map, AS_PROGRAM, 8, mpu401_device )
+ AM_RANGE(0x0000, 0x001f) AM_READWRITE(regs_mode2_r, regs_mode2_w)
+ AM_RANGE(0x0020, 0x0021) AM_READWRITE(asic_r, asic_w)
+ AM_RANGE(0x0080, 0x00ff) AM_RAM // on-chip RAM
+ AM_RANGE(0x0800, 0x0fff) AM_RAM // external RAM
+ AM_RANGE(0xf000, 0xffff) AM_ROM AM_REGION(ROM_TAG, 0)
+ADDRESS_MAP_END
+
+static ADDRESS_MAP_START( mpu401_io_map, AS_IO, 8, mpu401_device )
+ AM_RANGE(M6801_PORT1, M6801_PORT1) AM_READWRITE(port1_r, port1_w)
+ AM_RANGE(M6801_PORT2, M6801_PORT2) AM_READWRITE(port2_r, port2_w)
+ADDRESS_MAP_END
+
+MACHINE_CONFIG_FRAGMENT( mpu401 )
+ MCFG_CPU_ADD(M6801_TAG, M6801, 4000000) /* 4 MHz as per schematics */
+ MCFG_CPU_PROGRAM_MAP(mpu401_map)
+ MCFG_CPU_IO_MAP(mpu401_io_map)
+ MCFG_M6801_SER_TX(DEVWRITELINE(MIDIOUT_TAG, midi_port_device, write_txd))
+
+ MCFG_MIDI_PORT_ADD(MIDIIN_TAG, midiin_slot, "midiin")
+ MCFG_MIDI_RX_HANDLER(DEVWRITELINE(DEVICE_SELF, mpu401_device, midi_rx_w))
+
+ MCFG_MIDI_PORT_ADD(MIDIOUT_TAG, midiout_slot, "midiout")
+MACHINE_CONFIG_END
+
+ROM_START( mpu401 )
+ ROM_REGION(0x1000, ROM_TAG, 0)
+ ROM_LOAD( "roland_6801v0b55p.bin", 0x000000, 0x001000, CRC(65d3a151) SHA1(00efbfb96aeb997b69bb16981c6751d3c784bb87) )
+ROM_END
+
+//**************************************************************************
+// GLOBAL VARIABLES
+//**************************************************************************
+
+const device_type MPU401 = &device_creator<mpu401_device>;
+
+//-------------------------------------------------
+// machine_config_additions - device-specific
+// machine configurations
+//-------------------------------------------------
+
+machine_config_constructor mpu401_device::device_mconfig_additions() const
+{
+ return MACHINE_CONFIG_NAME( mpu401 );
+}
+
+//-------------------------------------------------
+// rom_region - device-specific ROM region
+//-------------------------------------------------
+
+const rom_entry *mpu401_device::device_rom_region() const
+{
+ return ROM_NAME( mpu401 );
+}
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// mpu401_device - constructor
+//-------------------------------------------------
+
+mpu401_device::mpu401_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) :
+ device_t(mconfig, MPU401, "Roland MPU-401 I/O box", tag, owner, clock, "mpu401", __FILE__),
+ m_ourcpu(*this, M6801_TAG),
+ write_irq(*this)
+{
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void mpu401_device::device_start()
+{
+ write_irq.resolve_safe();
+ m_timer = timer_alloc(0, NULL);
+}
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void mpu401_device::device_reset()
+{
+ m_port2 = 0xff & ~(P2_SRCK_OUT | P2_MIDI_IN); // prevent spurious reception
+ m_command = 0;
+ m_mpudata = 0;
+ m_gatearrstat = 0;
+
+ m_timer->adjust(attotime::zero, 0, attotime::from_hz(31250*8));
+}
+
+//-------------------------------------------------
+// device_timer - called when our device timer expires
+//-------------------------------------------------
+
+void mpu401_device::device_timer(emu_timer &timer, device_timer_id tid, int param, void *ptr)
+{
+ m_ourcpu->m6801_clock_serial();
+}
+
+READ8_MEMBER(mpu401_device::regs_mode2_r)
+{
+ switch (offset)
+ {
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ case 0xf:
+// printf("MPU401: read @ unk %x (PC=%x)\n", offset, space.device().safe_pc());
+ break;
+
+ default:
+ return m_ourcpu->m6801_io_r(space, offset);
+ }
+
+ return 0xff;
+}
+
+WRITE8_MEMBER(mpu401_device::regs_mode2_w)
+{
+ switch (offset)
+ {
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ case 0xf:
+// printf("MPU401: %02x @ unk %x (PC=%x)\n", data, offset, space.device().safe_pc());
+ break;
+
+ default:
+ return m_ourcpu->m6801_io_w(space, offset, data);
+ }
+}
+
+READ8_MEMBER(mpu401_device::port1_r)
+{
+ return 0xff;
+}
+
+WRITE8_MEMBER(mpu401_device::port1_w)
+{
+// printf("port1_w: %02x met %x syncout %x DSRD %d DRRD %d\n", data, data & 3, (data>>3) & 3, (data>>6) & 1, (data>>7) & 1);
+}
+
+READ8_MEMBER(mpu401_device::port2_r)
+{
+// printf("Read P2 (PC=%x)\n", space.device().safe_pc());
+ return m_port2;
+}
+
+WRITE8_MEMBER(mpu401_device::port2_w)
+{
+// printf("port2_w: %02x SYCOUT %d SYCIN %d SRCK %d MIDI OUT %d\n", data, (data & 1), (data>>1) & 1, (data>>2) & 1, (data>>4) & 1);
+}
+
+READ8_MEMBER(mpu401_device::mpu_r)
+{
+// printf("mpu_r @ %d\n", offset);
+
+ if (offset == 1) // status
+ {
+ return m_gatearrstat;
+ }
+ else // data
+ {
+ write_irq(CLEAR_LINE);
+ m_gatearrstat |= STAT_RX_EMPTY;
+ return m_mpudata;
+ }
+}
+
+WRITE8_MEMBER(mpu401_device::mpu_w)
+{
+// printf("%02x to MPU-401 @ %d\n", data, offset);
+ m_command = data;
+ m_gatearrstat |= STAT_TX_FULL;
+
+ if (offset == 1)
+ {
+ m_gatearrstat |= STAT_CMD_PORT;
+ }
+ else
+ {
+ m_gatearrstat &= ~STAT_CMD_PORT;
+ }
+}
+
+READ8_MEMBER(mpu401_device::asic_r)
+{
+ if (offset == 0)
+ {
+ m_gatearrstat &= ~STAT_TX_FULL;
+ return m_command;
+ }
+ else if (offset == 1)
+ {
+ return m_gatearrstat;
+ }
+
+ return 0xff;
+}
+
+WRITE8_MEMBER(mpu401_device::asic_w)
+{
+// printf("MPU401: %02x to gate array @ %d\n", data, offset);
+
+ if (offset == 1)
+ {
+ m_mpudata = data;
+ m_gatearrstat &= ~STAT_RX_EMPTY;
+ write_irq(ASSERT_LINE);
+ }
+}
+
+// MIDI receive
+WRITE_LINE_MEMBER( mpu401_device::midi_rx_w )
+{
+ if (state == ASSERT_LINE)
+ {
+ m_port2 |= P2_MIDI_IN;
+ }
+ else
+ {
+ m_port2 &= ~P2_MIDI_IN;
+ }
+}