summaryrefslogblamecommitdiffstatshomepage
path: root/src/devices/machine/mpu401.cpp
blob: b9429aa8596578db1f59b792463735ccd6a4516c (plain) (tree)
1
2
3
4
5
6
7
8
9
10

                               




                                                                            
 

                                                                          
 
                                              
 



                                                         
 

                                                                  
                                           


                                      
 


                                                                                  
 


                                                                             
 






                                                                            
                
                           
                          
 



                             
 




                          
 


                                                                                                                                
 

                                                

                                                                                                     






                                                     

                                                                                                     
 
 

                                      
                                                                                                                                                                                                                                                                                                                                     





                                                                            
                                                                             

                                                   
                                                 

                                                   
                                                       



                                                                                

                                                             
                                                                              


                                                                




                                                   
                                                              











                                                                            
                                                                                                               
                                                     











                                                   
                                          







                                                   
                                                                                     


                          










                                                                                             










                                         
                                                                                         

















                                                                   
                                                                                               













                                                                         
                                                                                                                             



                                    
                                                                 




                                     
                                                                                                                                    



                                  
                                   
 
                                     


                                     
                                   








                                               
                                                   






                                               



                                                








                                               
                             








                                     
                                                              








                                                











                                             
// license:BSD-3-Clause
// copyright-holders:R. Belmont
/***************************************************************************

  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 "emu.h"
#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

void mpu401_device::mpu401_map(address_map &map)
{
	map(0x0000, 0x001f).rw(FUNC(mpu401_device::regs_mode2_r), FUNC(mpu401_device::regs_mode2_w));
	map(0x0020, 0x0021).rw(FUNC(mpu401_device::asic_r), FUNC(mpu401_device::asic_w));
	map(0x0080, 0x00ff).ram(); // on-chip RAM
	map(0x0800, 0x0fff).ram(); // external RAM
	map(0xf000, 0xffff).rom().region(ROM_TAG, 0);
}

void mpu401_device::mpu401_io_map(address_map &map)
{
	map(M6801_PORT1, M6801_PORT1).rw(FUNC(mpu401_device::port1_r), FUNC(mpu401_device::port1_w));
	map(M6801_PORT2, M6801_PORT2).rw(FUNC(mpu401_device::port2_r), FUNC(mpu401_device::port2_w));
}

ROM_START( mpu401 )
	ROM_REGION(0x1000, ROM_TAG, 0)
	ROM_LOAD( "roland__6801v0b55p__15179222.bin", 0x000000, 0x001000, CRC(65d3a151) SHA1(00efbfb96aeb997b69bb16981c6751d3c784bb87) ) /* Mask MCU; Label: "Roland // 6801V0B55P // 5A1 JAPAN // 15179222"; This is the final version (1.5A) of the mpu401 firmware; version is located at offsets 0x649 (0x15) and 0x64f (0x01) */
ROM_END

//**************************************************************************
//  GLOBAL VARIABLES
//**************************************************************************

DEFINE_DEVICE_TYPE(MPU401, mpu401_device, "mpu401", "Roland MPU-401 I/O box")

//-------------------------------------------------
//  device_add_mconfig - add device configuration
//-------------------------------------------------

MACHINE_CONFIG_START(mpu401_device::device_add_mconfig)
	MCFG_DEVICE_ADD(M6801_TAG, M6801, 4000000) /* 4 MHz as per schematics */
	MCFG_DEVICE_PROGRAM_MAP(mpu401_map)
	MCFG_DEVICE_IO_MAP(mpu401_io_map)
	MCFG_M6801_SER_TX(WRITELINE(MIDIOUT_TAG, midi_port_device, write_txd))

	MCFG_MIDI_PORT_ADD(MIDIIN_TAG, midiin_slot, "midiin")
	MCFG_MIDI_RX_HANDLER(WRITELINE(DEVICE_SELF, mpu401_device, midi_rx_w))

	MCFG_MIDI_PORT_ADD(MIDIOUT_TAG, midiout_slot, "midiout")
MACHINE_CONFIG_END

//-------------------------------------------------
//  rom_region - device-specific ROM region
//-------------------------------------------------

const tiny_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_t clock) :
	device_t(mconfig, MPU401, tag, owner, clock),
	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, nullptr);
}

//-------------------------------------------------
//  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:
//          logerror("MPU401: read @ unk %x %s\n", offset, machine().describe_context());
			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:
//          logerror("MPU401: %02x @ unk %x %s\n", data, offset, machine().describe_context());
			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("%s Read P2\n", machine().describe_context().c_str());
	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;
	}
}