trunk/src/mess/machine/mpu401.c
| r0 | r24848 | |
| 1 | /*************************************************************************** |
| 2 | |
| 3 | Roland MPU-401 core |
| 4 | |
| 5 | This emulates the MPU-401 external box with the 6801, ASIC, and RAM in it. |
| 6 | |
| 7 | We do it this way to facilitate the various PC, Apple II, C64, and other |
| 8 | possible hookups. |
| 9 | |
| 10 | 6801 GPIO port hookups (from the schematics) |
| 11 | |
| 12 | P10 / P11 / P12: drive the metronome and speaker |
| 13 | P13 / P14 / P15: drive 3 pins on the SYNC OUT connector |
| 14 | P16: to DSRD on gate array |
| 15 | P17: to DRRD on gate array |
| 16 | |
| 17 | P20: to SYC OUT on gate array |
| 18 | P21: to SYC IN on gate array, pulled up to Vcc via 4.7K resistor |
| 19 | programmed as output of timer (OLVL) |
| 20 | P22: to SRCK on gate array, inverted |
| 21 | P23: MIDI IN serial data (SCI in) |
| 22 | P24: MIDI OUT serial data (SCI out) |
| 23 | |
| 24 | ASIC addresses from the 6801: |
| 25 | 0x20: (r) read pending byte from the PC (w) apparently nothing |
| 26 | 0x21: (r) ASIC status, see STAT_xxx bits below (w) send new byte to PC data port |
| 27 | |
| 28 | Theory of operation: 6801's timer/counter is set up to drive a pulse stream |
| 29 | out P21 to the ASIC's SYC IN pin. The ASIC in turn generates the MIDI baud |
| 30 | rate (times 8) and returns that on pin P22. |
| 31 | |
| 32 | The 6801 is believed to run in mode 2, based on a combination of the |
| 33 | schematics and the behavior (ie, internal RAM from 80-FF is clearly |
| 34 | present from the program's behavior, and ports 3/4 are obviously external |
| 35 | address/data buses) |
| 36 | |
| 37 | ***************************************************************************/ |
| 38 | |
| 39 | #include "machine/mpu401.h" |
| 40 | |
| 41 | #define M6801_TAG "mpu6801" |
| 42 | #define ROM_TAG "mpurom" |
| 43 | |
| 44 | #define P2_SYNC_OUT (0x01) |
| 45 | #define P2_SYNC_IN (0x02) |
| 46 | #define P2_SRCK_OUT (0x04) |
| 47 | #define P2_MIDI_IN (0x08) |
| 48 | #define P2_MIDI_OUT (0x10) |
| 49 | |
| 50 | #define STAT_CMD_PORT (0x01) // set if the new byte indicated by TX FULL was written to the command port, clear for data port |
| 51 | #define STAT_TX_FULL (0x40) // indicates the PC has written a new byte we haven't read yet |
| 52 | #define STAT_RX_EMPTY (0x80) // indicates we've written a new byte the PC hasn't read yet |
| 53 | |
| 54 | static ADDRESS_MAP_START( mpu401_map, AS_PROGRAM, 8, mpu401_device ) |
| 55 | AM_RANGE(0x0000, 0x001f) AM_READWRITE(regs_mode2_r, regs_mode2_w) |
| 56 | AM_RANGE(0x0020, 0x0021) AM_READWRITE(asic_r, asic_w) |
| 57 | AM_RANGE(0x0080, 0x00ff) AM_RAM // on-chip RAM |
| 58 | AM_RANGE(0x0800, 0x0fff) AM_RAM // external RAM |
| 59 | AM_RANGE(0xf000, 0xffff) AM_ROM AM_REGION(ROM_TAG, 0) |
| 60 | ADDRESS_MAP_END |
| 61 | |
| 62 | static ADDRESS_MAP_START( mpu401_io_map, AS_IO, 8, mpu401_device ) |
| 63 | AM_RANGE(M6801_PORT1, M6801_PORT1) AM_READWRITE(port1_r, port1_w) |
| 64 | AM_RANGE(M6801_PORT2, M6801_PORT2) AM_READWRITE(port2_r, port2_w) |
| 65 | ADDRESS_MAP_END |
| 66 | |
| 67 | MACHINE_CONFIG_FRAGMENT( mpu401 ) |
| 68 | MCFG_CPU_ADD(M6801_TAG, M6801, 4000000) /* 4 MHz as per schematics */ |
| 69 | MCFG_CPU_PROGRAM_MAP(mpu401_map) |
| 70 | MCFG_CPU_IO_MAP(mpu401_io_map) |
| 71 | MACHINE_CONFIG_END |
| 72 | |
| 73 | ROM_START( mpu401 ) |
| 74 | ROM_REGION(0x1000, ROM_TAG, 0) |
| 75 | ROM_LOAD( "roland_6801v0b55p.bin", 0x000000, 0x001000, CRC(65d3a151) SHA1(00efbfb96aeb997b69bb16981c6751d3c784bb87) ) |
| 76 | ROM_END |
| 77 | |
| 78 | //************************************************************************** |
| 79 | // GLOBAL VARIABLES |
| 80 | //************************************************************************** |
| 81 | |
| 82 | const device_type MPU401 = &device_creator<mpu401_device>; |
| 83 | |
| 84 | //------------------------------------------------- |
| 85 | // machine_config_additions - device-specific |
| 86 | // machine configurations |
| 87 | //------------------------------------------------- |
| 88 | |
| 89 | machine_config_constructor mpu401_device::device_mconfig_additions() const |
| 90 | { |
| 91 | return MACHINE_CONFIG_NAME( mpu401 ); |
| 92 | } |
| 93 | |
| 94 | //------------------------------------------------- |
| 95 | // rom_region - device-specific ROM region |
| 96 | //------------------------------------------------- |
| 97 | |
| 98 | const rom_entry *mpu401_device::device_rom_region() const |
| 99 | { |
| 100 | return ROM_NAME( mpu401 ); |
| 101 | } |
| 102 | |
| 103 | //************************************************************************** |
| 104 | // LIVE DEVICE |
| 105 | //************************************************************************** |
| 106 | |
| 107 | //------------------------------------------------- |
| 108 | // mpu401_device - constructor |
| 109 | //------------------------------------------------- |
| 110 | |
| 111 | mpu401_device::mpu401_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : |
| 112 | device_t(mconfig, MPU401, "Roland MPU-401", tag, owner, clock, "mpu401", __FILE__), |
| 113 | m_ourcpu(*this, M6801_TAG), |
| 114 | write_irq(*this) |
| 115 | { |
| 116 | } |
| 117 | |
| 118 | //------------------------------------------------- |
| 119 | // device_start - device-specific startup |
| 120 | //------------------------------------------------- |
| 121 | |
| 122 | void mpu401_device::device_start() |
| 123 | { |
| 124 | write_irq.resolve_safe(); |
| 125 | } |
| 126 | |
| 127 | //------------------------------------------------- |
| 128 | // device_reset - device-specific reset |
| 129 | //------------------------------------------------- |
| 130 | |
| 131 | void mpu401_device::device_reset() |
| 132 | { |
| 133 | m_port2 = 0xff & ~P2_SRCK_OUT; |
| 134 | m_command = 0; |
| 135 | m_mpudata = 0; |
| 136 | m_gatearrstat = 0; |
| 137 | } |
| 138 | |
| 139 | READ8_MEMBER(mpu401_device::regs_mode2_r) |
| 140 | { |
| 141 | switch (offset) |
| 142 | { |
| 143 | case 4: |
| 144 | case 5: |
| 145 | case 6: |
| 146 | case 7: |
| 147 | case 0xf: |
| 148 | printf("MPU401: read @ unk %x (PC=%x)\n", offset, space.device().safe_pc()); |
| 149 | break; |
| 150 | |
| 151 | default: |
| 152 | return m_ourcpu->m6801_io_r(space, offset); |
| 153 | } |
| 154 | |
| 155 | return 0xff; |
| 156 | } |
| 157 | |
| 158 | WRITE8_MEMBER(mpu401_device::regs_mode2_w) |
| 159 | { |
| 160 | switch (offset) |
| 161 | { |
| 162 | case 4: |
| 163 | case 5: |
| 164 | case 6: |
| 165 | case 7: |
| 166 | case 0xf: |
| 167 | printf("MPU401: %02x @ unk %x (PC=%x)\n", data, offset, space.device().safe_pc()); |
| 168 | break; |
| 169 | |
| 170 | default: |
| 171 | return m_ourcpu->m6801_io_w(space, offset, data); |
| 172 | } |
| 173 | } |
| 174 | |
| 175 | READ8_MEMBER(mpu401_device::port1_r) |
| 176 | { |
| 177 | return 0xff; |
| 178 | } |
| 179 | |
| 180 | WRITE8_MEMBER(mpu401_device::port1_w) |
| 181 | { |
| 182 | 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); |
| 183 | } |
| 184 | |
| 185 | READ8_MEMBER(mpu401_device::port2_r) |
| 186 | { |
| 187 | printf("Read P2 (PC=%x)\n", space.device().safe_pc()); |
| 188 | return m_port2; |
| 189 | } |
| 190 | |
| 191 | WRITE8_MEMBER(mpu401_device::port2_w) |
| 192 | { |
| 193 | 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); |
| 194 | } |
| 195 | |
| 196 | READ8_MEMBER(mpu401_device::mpu_r) |
| 197 | { |
| 198 | // printf("mpu_r @ %d\n", offset); |
| 199 | |
| 200 | if (offset == 1) // status |
| 201 | { |
| 202 | return m_gatearrstat; |
| 203 | } |
| 204 | else // data |
| 205 | { |
| 206 | write_irq(CLEAR_LINE); |
| 207 | m_gatearrstat |= STAT_RX_EMPTY; |
| 208 | return m_mpudata; |
| 209 | } |
| 210 | } |
| 211 | |
| 212 | WRITE8_MEMBER(mpu401_device::mpu_w) |
| 213 | { |
| 214 | // printf("%02x to MPU-401 @ %d\n", data, offset); |
| 215 | m_command = data; |
| 216 | m_gatearrstat |= STAT_TX_FULL; |
| 217 | |
| 218 | if (offset == 1) |
| 219 | { |
| 220 | m_gatearrstat |= STAT_CMD_PORT; |
| 221 | } |
| 222 | } |
| 223 | |
| 224 | READ8_MEMBER(mpu401_device::asic_r) |
| 225 | { |
| 226 | if (offset == 0) |
| 227 | { |
| 228 | m_gatearrstat &= ~STAT_TX_FULL; |
| 229 | return m_command; |
| 230 | } |
| 231 | else if (offset == 1) |
| 232 | { |
| 233 | return m_gatearrstat; |
| 234 | } |
| 235 | |
| 236 | return 0xff; |
| 237 | } |
| 238 | |
| 239 | WRITE8_MEMBER(mpu401_device::asic_w) |
| 240 | { |
| 241 | // printf("MPU401: %02x to gate array @ %d\n", data, offset); |
| 242 | |
| 243 | if (offset == 1) |
| 244 | { |
| 245 | m_mpudata = data; |
| 246 | m_gatearrstat &= ~STAT_RX_EMPTY; |
| 247 | write_irq(ASSERT_LINE); |
| 248 | } |
| 249 | } |
| 250 | |
trunk/src/mess/machine/mpu401.h
| r0 | r24848 | |
| 1 | #pragma once |
| 2 | |
| 3 | #ifndef __MPU401_H__ |
| 4 | #define __MPU401_H__ |
| 5 | |
| 6 | #include "emu.h" |
| 7 | #include "cpu/m6800/m6800.h" |
| 8 | |
| 9 | #define MCFG_MPU401_ADD(_tag, _irqf ) \ |
| 10 | MCFG_DEVICE_ADD(_tag, MPU401, 0) \ |
| 11 | MCFG_IRQ_FUNC(_irqf) |
| 12 | |
| 13 | #define MCFG_IRQ_FUNC(_irqf) \ |
| 14 | downcast<mpu401_device *>(device)->set_irqf(DEVCB2_##_irqf); |
| 15 | |
| 16 | //************************************************************************** |
| 17 | // TYPE DEFINITIONS |
| 18 | //************************************************************************** |
| 19 | |
| 20 | class mpu401_device : public device_t |
| 21 | { |
| 22 | public: |
| 23 | // construction/destruction |
| 24 | mpu401_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); |
| 25 | |
| 26 | // optional information overrides |
| 27 | virtual machine_config_constructor device_mconfig_additions() const; |
| 28 | |
| 29 | required_device<m6801_cpu_device> m_ourcpu; |
| 30 | |
| 31 | template<class _write> void set_irqf(_write wr) |
| 32 | { |
| 33 | write_irq.set_callback(wr); |
| 34 | } |
| 35 | |
| 36 | devcb2_write_line write_irq; |
| 37 | |
| 38 | DECLARE_READ8_MEMBER(regs_mode2_r); |
| 39 | DECLARE_WRITE8_MEMBER(regs_mode2_w); |
| 40 | DECLARE_READ8_MEMBER(asic_r); |
| 41 | DECLARE_WRITE8_MEMBER(asic_w); |
| 42 | DECLARE_READ8_MEMBER(port1_r); |
| 43 | DECLARE_WRITE8_MEMBER(port1_w); |
| 44 | DECLARE_READ8_MEMBER(port2_r); |
| 45 | DECLARE_WRITE8_MEMBER(port2_w); |
| 46 | |
| 47 | // public API - call for reads/writes at I/O 330/331 on PC, C0n0/C0n1 on Apple II, etc. |
| 48 | DECLARE_READ8_MEMBER(mpu_r); |
| 49 | DECLARE_WRITE8_MEMBER(mpu_w); |
| 50 | |
| 51 | protected: |
| 52 | // device-level overrides |
| 53 | virtual void device_start(); |
| 54 | virtual void device_reset(); |
| 55 | virtual const rom_entry *device_rom_region() const; |
| 56 | |
| 57 | private: |
| 58 | UINT8 m_port2; |
| 59 | UINT8 m_command; |
| 60 | UINT8 m_mpudata; |
| 61 | UINT8 m_gatearrstat; |
| 62 | }; |
| 63 | |
| 64 | // device type definition |
| 65 | extern const device_type MPU401; |
| 66 | |
| 67 | #endif /* __MPU401_H__ */ |
| 68 | |
| 69 | |
trunk/src/mess/machine/isa_mpu401.c
| r24847 | r24848 | |
| 11 | 11 | #include "isa_mpu401.h" |
| 12 | 12 | #include "machine/pic8259.h" |
| 13 | 13 | |
| 14 | #define MPU_CORE_TAG "mpu401" |
| 15 | |
| 16 | MACHINE_CONFIG_FRAGMENT( isa8mpu401 ) |
| 17 | MCFG_MPU401_ADD(MPU_CORE_TAG, WRITELINE(isa8_mpu401_device, mpu_irq_out)) |
| 18 | MACHINE_CONFIG_END |
| 19 | |
| 14 | 20 | /* |
| 15 | 21 | DIP-SWs |
| 16 | 22 | 1-2-3-4 |
| r24847 | r24848 | |
| 29 | 35 | 1 irq7 |
| 30 | 36 | */ |
| 31 | 37 | |
| 32 | | READ8_MEMBER( isa8_mpu401_device::mpu401_r ) |
| 38 | WRITE_LINE_MEMBER( isa8_mpu401_device::mpu_irq_out ) |
| 33 | 39 | { |
| 34 | | UINT8 res; |
| 35 | | |
| 36 | | if(offset == 0) // data |
| 37 | | { |
| 38 | | res = 0xff; |
| 39 | | } |
| 40 | | else // status |
| 41 | | { |
| 42 | | res = 0x3f | 0x80; // bit 7 queue empty (DSR), bit 6 DRR (Data Receive Ready?) |
| 43 | | } |
| 44 | | |
| 45 | | return res; |
| 46 | 40 | } |
| 47 | 41 | |
| 48 | | WRITE8_MEMBER( isa8_mpu401_device::mpu401_w ) |
| 49 | | { |
| 50 | | if(offset == 0) // data |
| 51 | | { |
| 52 | | printf("%02x %02x\n",offset,data); |
| 53 | | } |
| 54 | | else // command |
| 55 | | { |
| 56 | | printf("%02x %02x\n",offset,data); |
| 57 | | |
| 58 | | switch(data) |
| 59 | | { |
| 60 | | case 0xff: // reset |
| 61 | | //m_isa->irq2_w(1); |
| 62 | | break; |
| 63 | | } |
| 64 | | } |
| 65 | | |
| 66 | | } |
| 67 | | |
| 68 | 42 | //************************************************************************** |
| 69 | 43 | // GLOBAL VARIABLES |
| 70 | 44 | //************************************************************************** |
| 71 | 45 | |
| 72 | 46 | const device_type ISA8_MPU401 = &device_creator<isa8_mpu401_device>; |
| 73 | 47 | |
| 48 | //------------------------------------------------- |
| 49 | // machine_config_additions - device-specific |
| 50 | // machine configurations |
| 51 | //------------------------------------------------- |
| 74 | 52 | |
| 53 | machine_config_constructor isa8_mpu401_device::device_mconfig_additions() const |
| 54 | { |
| 55 | return MACHINE_CONFIG_NAME( isa8mpu401 ); |
| 56 | } |
| 57 | |
| 58 | |
| 75 | 59 | //************************************************************************** |
| 76 | 60 | // LIVE DEVICE |
| 77 | 61 | //************************************************************************** |
| r24847 | r24848 | |
| 81 | 65 | //------------------------------------------------- |
| 82 | 66 | |
| 83 | 67 | isa8_mpu401_device::isa8_mpu401_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) |
| 84 | | : device_t(mconfig, ISA8_MPU401, "Roland MPU-401 Sound Card", tag, owner, clock, "isa_mpu401", __FILE__), |
| 85 | | device_isa8_card_interface( mconfig, *this ) |
| 68 | : device_t(mconfig, ISA8_MPU401, "Roland MPU-401 MIDI Interface", tag, owner, clock, "isa_mpu401", __FILE__), |
| 69 | device_isa8_card_interface( mconfig, *this ), |
| 70 | m_mpu401(*this, MPU_CORE_TAG) |
| 86 | 71 | { |
| 87 | 72 | } |
| 88 | 73 | |
| r24847 | r24848 | |
| 93 | 78 | void isa8_mpu401_device::device_start() |
| 94 | 79 | { |
| 95 | 80 | set_isa_device(); |
| 96 | | m_isa->install_device(0x330, 0x0331, 0, 0, read8_delegate(FUNC(isa8_mpu401_device::mpu401_r), this), write8_delegate(FUNC(isa8_mpu401_device::mpu401_w), this)); |
| 81 | |
| 82 | m_isa->install_device(0x330, 0x0331, 0, 0, READ8_DEVICE_DELEGATE(m_mpu401, mpu401_device, mpu_r), WRITE8_DEVICE_DELEGATE(m_mpu401, mpu401_device, mpu_w)); |
| 97 | 83 | } |
| 98 | 84 | |
| 99 | 85 | //------------------------------------------------- |