diff options
Diffstat (limited to 'src/emu/machine/mpu401.c')
-rw-r--r-- | src/emu/machine/mpu401.c | 287 |
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; + } +} |