- Add preliminary Yamaha YM3802 MIDI controller

- Add X68000 expansion device using the YM3802
This commit is contained in:
mahlemiut 2017-06-21 18:34:36 +12:00
parent e6f86fa673
commit 03f21234b8
9 changed files with 601 additions and 3 deletions

View File

@ -2631,6 +2631,8 @@ if (BUSES["X68K"]~=null) then
MAME_DIR .. "src/devices/bus/x68k/x68k_neptunex.h",
MAME_DIR .. "src/devices/bus/x68k/x68k_scsiext.cpp",
MAME_DIR .. "src/devices/bus/x68k/x68k_scsiext.h",
MAME_DIR .. "src/devices/bus/x68k/x68k_midi.cpp",
MAME_DIR .. "src/devices/bus/x68k/x68k_midi.h",
}
end

View File

@ -2681,6 +2681,18 @@ if (MACHINES["YM2148"]~=null) then
}
end
---------------------------------------------------
--
--@src/devices/machine/ym3802.h,MACHINES["YM3802"] = true
---------------------------------------------------
if (MACHINES["YM3802"]~=null) then
files {
MAME_DIR .. "src/devices/machine/ym3802.cpp",
MAME_DIR .. "src/devices/machine/ym3802.h",
}
end
---------------------------------------------------
--
--@src/devices/machine/z80ctc.h,MACHINES["Z80CTC"] = true

View File

@ -0,0 +1,66 @@
// license:BSD-3-Clause
// copyright-holders:Barry Rodewald
/*
* x68k_midi.c
*
* X68000 MIDI interface - YM3802
*
*/
#include "emu.h"
#include "bus/midi/midi.h"
#include "x68k_midi.h"
//**************************************************************************
// DEVICE DEFINITIONS
//**************************************************************************
DEFINE_DEVICE_TYPE(X68K_MIDI, x68k_midi_device, "x68k_midi", "X68000 MIDI Interface")
MACHINE_CONFIG_MEMBER( x68k_midi_device::device_add_mconfig )
MCFG_DEVICE_ADD("midi", YM3802, XTAL_1MHz) // clock is unknown
MCFG_YM3802_TXD_HANDLER(DEVWRITELINE("mdout",midi_port_device,write_txd))
MCFG_YM3802_IRQ_HANDLER(WRITELINE(x68k_midi_device,irq_w))
MCFG_MIDI_PORT_ADD("mdin", midiin_slot, "midiin")
MCFG_MIDI_PORT_ADD("mdout", midiout_slot, "midiout")
// MCFG_MIDI_PORT_ADD("mdthru", midiout_slot, "midiout")
// TODO: Add serial data handlers
MACHINE_CONFIG_END
x68k_midi_device::x68k_midi_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: device_t(mconfig, X68K_MIDI, tag, owner, clock)
, device_x68k_expansion_card_interface(mconfig, *this)
, m_slot(nullptr)
, m_midi(*this, "midi")
{
}
void x68k_midi_device::device_start()
{
device_t* cpu = machine().device("maincpu");
address_space& space = cpu->memory().space(AS_PROGRAM);
m_slot = dynamic_cast<x68k_expansion_slot_device *>(owner());
space.install_readwrite_handler(0xeafa00,0xeafa0f,read8_delegate(FUNC(x68k_midi_device::x68k_midi_reg_r),this),write8_delegate(FUNC(x68k_midi_device::x68k_midi_reg_w),this),0x00ff00ff);
}
void x68k_midi_device::device_reset()
{
}
READ8_MEMBER(x68k_midi_device::x68k_midi_reg_r)
{
return m_midi->read(space, offset);
}
WRITE8_MEMBER(x68k_midi_device::x68k_midi_reg_w)
{
m_midi->write(space, offset, data);
}
void x68k_midi_device::irq_w(int state)
{
set_vector(MIDI_IRQ_VECTOR | (m_midi->vector() & 0x1f));
m_slot->irq4_w(state); // selectable between IRQ2 and IRQ4
}

View File

@ -0,0 +1,48 @@
// license:BSD-3-Clause
// copyright-holders:Barry Rodewald
/*
* x68k_midi.h
*
* X68000 MIDI expansion card
*
*/
#ifndef MAME_BUS_X68K_X68K_MIDI_H
#define MAME_BUS_X68K_X68K_MIDI_H
#pragma once
#include "machine/ym3802.h"
#include "x68kexp.h"
#define MIDI_IRQ_VECTOR 0x80 // does not seem to use the YM3802's vector registers
class x68k_midi_device : public device_t,
public device_x68k_expansion_card_interface
{
public:
// construction/destruction
x68k_midi_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
// optional information overrides
virtual void device_add_mconfig(machine_config &config) override;
DECLARE_READ8_MEMBER(x68k_midi_reg_r);
DECLARE_WRITE8_MEMBER(x68k_midi_reg_w);
protected:
// device-level overrides
virtual void device_start() override;
virtual void device_reset() override;
private:
x68k_expansion_slot_device *m_slot;
required_device<ym3802_device> m_midi;
void irq_w(int state);
};
// device type definition
DECLARE_DEVICE_TYPE(X68K_MIDI, x68k_midi_device)
#endif // MAME_BUS_X68K_X68K_MIDI_H

View File

@ -105,9 +105,15 @@ public:
// reset
virtual void x68k_reset_w() { }
void set_vector(uint8_t vector) { m_vector = vector; }
uint8_t vector() { return m_vector; }
protected:
device_x68k_expansion_card_interface(const machine_config &mconfig, device_t &device);
private:
uint8_t m_vector;
};
@ -131,6 +137,8 @@ public:
DECLARE_WRITE_LINE_MEMBER( nmi_w );
DECLARE_WRITE_LINE_MEMBER( reset_w );
uint8_t vector() { return m_card->vector(); }
protected:
// device-level overrides
virtual void device_start() override;

View File

@ -0,0 +1,286 @@
// license:BSD-3-Clause
// copyright-holders:Barry Rodewald
/*
* ym3802.c - Yamaha MCS MIDI Communication and Service Controller
*
* TODO:
* - Receive serial data
* - Transmit Idle detection
* - IRx/ITx (used for MIDI system messages)
* - FSK modulation
* - Timers
* - Interrupts (except for Tx Buffer Empty)
*/
#include "emu.h"
#include "ym3802.h"
DEFINE_DEVICE_TYPE(YM3802, ym3802_device, "ym3802", "Yamaha YM3802 MCS MIDI Communication and Service Controller")
ym3802_device::ym3802_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: device_t(mconfig, YM3802, tag, owner, clock)
, device_serial_interface(mconfig, *this)
, m_irq_handler(*this)
, m_txd_handler(*this)
, m_rxd_handler(*this)
, m_reg(REG_MAX)
, m_wdr(0)
, m_irq_status(0)
, m_vector(0)
, m_clkm_rate(500000) // TODO: make these configurable
, m_clkf_rate(614400)
{
}
void ym3802_device::device_start()
{
m_irq_handler.resolve_safe();
m_txd_handler.resolve_safe();
m_rxd_handler.resolve_safe(0xff);
m_clock_timer = timer_alloc(TIMER_SYSTEM_CLOCK);
m_midi_timer = timer_alloc(TIMER_MIDI_CLOCK);
save_item(NAME(m_reg));
}
void ym3802_device::device_reset()
{
m_reg.clear();
reset_irq(0xff);
transmit_register_reset();
receive_register_reset();
reset_midi_timer();
set_comms_mode();
}
void ym3802_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
{
// TODO: support clock and timers
switch(id)
{
case TIMER_MIDI_CLOCK:
transmit_clk();
break;
}
}
void ym3802_device::set_irq(uint8_t irq)
{
uint8_t x;
m_irq_status |= (irq & m_reg[REG_IER]);
for(x=0;x<8;x++)
{
if(m_irq_status & (1 << x))
break;
}
m_vector = (m_reg[REG_IOR] & 0xe0) | (x << 1);
if(m_irq_status != 0)
m_irq_handler(ASSERT_LINE);
}
void ym3802_device::reset_irq(uint8_t irq)
{
m_irq_status &= ~irq;
if(m_irq_status == 0)
m_irq_handler(CLEAR_LINE);
}
void ym3802_device::transmit_clk()
{
if(m_reg[REG_TCR] & 0x01) // Tx Enable
{
if(!m_tx_fifo.empty())
{
if (is_transmit_register_empty())
{
transmit_register_setup(m_tx_fifo.front()); // start to send first byte in FIFO
m_tx_fifo.pop(); // and remove it from the FIFO
if(m_tx_fifo.empty())
set_irq(IRQ_FIFOTX_EMPTY);
}
}
/* if diserial has bits to send, make them so */
if (!is_transmit_register_empty())
{
uint8_t data = transmit_register_get_data_bit();
m_tx_busy = true;
m_txd_handler(data);
}
if (m_tx_fifo.empty() && is_transmit_register_empty())
m_tx_busy = false;
}
}
void ym3802_device::reset_midi_timer()
{
uint64_t rate;
uint8_t divisor = m_reg[REG_RRR] & 0x1f;
if(!(divisor & 0x10))
{
if(divisor & 0x08)
rate = m_clkm_rate / 32;
else
rate = m_clkm_rate / 16;
}
else
{
if(!(divisor & 0x08))
rate = m_clkf_rate / 32;
else
{
switch(divisor & 0x07)
{
case 0:
rate = m_clkf_rate / 64;
break;
case 1:
rate = m_clkf_rate / 128;
break;
case 2:
rate = m_clkf_rate / 256;
break;
case 3:
rate = m_clkf_rate / 512;
break;
case 4:
rate = m_clkf_rate / 1024;
break;
case 5:
rate = m_clkf_rate / 2048;
break;
case 6:
rate = m_clkf_rate / 4096;
break;
case 7:
rate = m_clkf_rate / 8192;
break;
}
}
}
if(rate != m_prev_rate)
m_midi_timer->adjust(attotime::from_hz(rate),0,attotime::from_hz(rate));
m_prev_rate = rate;
logerror("MIDI Timer rate set to %iHz\n",rate);
}
void ym3802_device::set_comms_mode()
{
uint8_t data_bits = (m_reg[REG_TMR] & 0x20) ? 7 : 8;
parity_t parity;
stop_bits_t stop_bits = (m_reg[REG_TMR] & 0x02) ? STOP_BITS_2 : STOP_BITS_1;
if(!(m_reg[REG_TMR] & 0x10)) // parity enable
parity = PARITY_NONE;
else
{
if(m_reg[REG_TMR] & 0x04)
parity = PARITY_ODD;
else
parity = PARITY_EVEN;
// TODO: 4-bit parity
}
set_data_frame(1, data_bits, parity, stop_bits);
logerror("MIDI comms set to 1 start bit, %i data bits, %s, parity = %i\n",data_bits, (stop_bits == STOP_BITS_2) ? "2 stop bits" : "1 stop bit", parity);
}
READ8_MEMBER(ym3802_device::read)
{
if(offset < 4)
{
if(offset == 3)
return m_wdr;
if(offset == 2)
return m_irq_status;
if(offset == 0)
return m_vector;
return m_reg[offset];
}
else
{
uint8_t bank = m_reg[REG_RGR] & 0x0f;
uint8_t ret = 0;
if(bank > 9)
return m_wdr;
switch(offset + (bank * 10))
{
case REG_TSR:
if(m_tx_fifo.empty())
ret |= 0x80;
if(m_tx_fifo.size() < 16)
ret |= 0x40;
if(m_tx_busy)
ret |= 0x01;
break;
default:
ret = m_reg[offset + (bank * 10)];
}
return ret;
}
}
WRITE8_MEMBER(ym3802_device::write)
{
m_wdr = data;
if(offset == 1)
{
m_reg[REG_RGR] = data & 0x0f;
if(data & 0x80)
device_reset();
logerror("MIDI: writing %02x to reg %i\n",data,offset);
}
if(offset == 3)
reset_irq(data);
if(offset > 4)
{
uint8_t bank = m_reg[REG_RGR] & 0x0f;
if(bank > 9)
return;
m_reg[offset + (bank * 10)] = data;
logerror("MIDI: writing %02x to reg %i\n",data,offset + (bank * 10));
switch(offset + (bank * 10))
{
case REG_IOR:
popmessage("IOR vector write %02\n",data);
break;
case REG_IER:
logerror("IER set to %02x\n",data);
break;
case REG_TMR:
set_comms_mode();
break;
case REG_TCR:
if(data & 0x01)
reset_midi_timer();
break;
case REG_TDR:
m_tx_fifo.push(data);
reset_irq(IRQ_FIFOTX_EMPTY);
break;
case REG_GTR_LOW:
m_general_counter = (m_general_counter & 0xff00) | data;
//popmessage("General counter set to %i\n",m_general_counter);
break;
case REG_GTR_HIGH:
m_general_counter = (m_general_counter & 0x00ff) | ((data & 0x3f) << 8);
//popmessage("General counter set to %i\n",m_general_counter);
break;
case REG_MTR_LOW:
m_midi_counter = (m_midi_counter & 0xff00) | data;
//popmessage("MIDI counter set to %i\n",m_midi_counter);
break;
case REG_MTR_HIGH:
m_midi_counter = (m_midi_counter & 0x00ff) | ((data & 0x3f) << 8);
//popmessage("MIDI counter set to %i\n",m_midi_counter);
break;
}
}
}

View File

@ -0,0 +1,164 @@
// license:BSD-3-Clause
// copyright-holders:Barry Rodewald
/*
* ym3802.h - Yamaha YM3802/YM3523 MCS MIDI Communication and Service Controller
*
* * Registers:
* reg0 : IVR (read-only)
* reg1 : RGR (bit 8 = reset, bits 0-3 = register bank select)
* reg2 : ISR (read-only)
* reg3 : ICR (write-only)
* reg4-reg7 banked registers
* reg4 : IOR DMR RRR RSR TRR TSR FSR SRR GTR(L) EDR
* reg5 : IMR DCR RMR RCR TMR TCR FCR SCR GTR(H) ---
* reg6 : IER DSR AMR RDR --- TDR CCR SPR(L) MTR(L) EOR/EIR
* reg7 : --- DNR ADR --- --- --- CDR SPR(H) MTR(H) ---
*/
#ifndef DEVICES_MACHINE_YM3802_H
#define DEVICES_MACHINE_YM3802_H
#pragma once
#include <queue>
#define MCFG_YM3802_IRQ_HANDLER(_devcb) \
devcb = &ym3802_device::set_irq_handler(*device, DEVCB_##_devcb);
#define MCFG_YM3802_TXD_HANDLER(_devcb) \
devcb = &ym3802_device::set_txd_handler(*device, DEVCB_##_devcb);
class ym3802_device : public device_t, public device_serial_interface
{
public:
// construction/destruction
ym3802_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
// static configuration helpers
template <class Object> static devcb_base &set_irq_handler(device_t &device, Object &&cb) { return downcast<ym3802_device &>(device).m_irq_handler.set_callback(std::forward<Object>(cb)); }
template <class Object> static devcb_base &set_txd_handler(device_t &device, Object &&cb) { return downcast<ym3802_device &>(device).m_txd_handler.set_callback(std::forward<Object>(cb)); }
DECLARE_READ8_MEMBER(read);
DECLARE_WRITE8_MEMBER(write);
uint8_t vector() { return m_vector; }
protected:
virtual void device_start() override;
virtual void device_reset() override;
virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override;
private:
enum
{
REG_IVR = 0, // Interrupt Vector (read only)
REG_RGR, // Register Group / System Control
REG_ISR, // Interrupt Service (read only)
REG_ICR, // Interrupt Clear (write only)
REG_IOR, // Interrupt Vector Offset Request
REG_IMR, // Interrupt Mode Control
REG_IER, // Interrupt Enable Request
REG_UNUSED1,
REG_DMR = 14, // Real Time Message Control
REG_DCR, // Real Time Message Request
REG_DSR, // FIFO IRx Data
REG_DNR, // FIFO IRx Control
REG_RRR = 24, // Rx Rate
REG_RMR, // Rx Mode
REG_AMR, // Address Hunter Maker
REG_ADR, // Address Hunter Device
REG_RSR = 34, // FIFO Rx Buffer Status
REG_RCR, // FIFO Rx Buffer Control
REG_RDR, // FIFO Rx Data
REG_UNUSED2,
REG_TRR = 44, // Tx Rate
REG_TMR, // Tx Mode
REG_UNUSED3,
REG_UNUSED4,
REG_TSR = 54, // FIFO Tx Status
REG_TCR, // FIFO Tx Control
REG_TDR, // FIFO Tx Data
REG_UNUSED5,
REG_FSR = 64, // FSK status
REG_FCR, // FSK control
REG_CCR, // Click Counter Control
REG_CDR, // Click Counter Data (7-bit)
REG_SRR = 74, // Recording Counter current value
REG_SCR, // Sequencer Control
REG_SPR_LOW, // Playback Counter (low 8-bits)
REG_SPR_HIGH, // Playback Counter (high 7-bits)
REG_GTR_LOW = 84, // General Timer (low 8-bits)
REG_GTR_HIGH, // General Timer (high 6-bits)
REG_MTR_LOW, // MIDI Clock Timer (low 8-bits)
REG_MTR_HIGH, // MIDI Clock Timer (high 6-bits)
REG_EDR = 94, // External I/O Direction
REG_EOR, // External I/O Output Data
REG_EIR, // External I/O Input Data
REG_UNUSED7,
REG_MAX = 100
};
enum
{
IRQ_MIDI_MSG = 0x01,
IRQ_CLICK = 0x02,
IRQ_MIDI_CLK = 0x02,
IRQ_PLAYBACK_COUNT = 0x04,
IRQ_RECORDING_COUNT = 0x08,
IRQ_OFFLINE = 0x10,
IRQ_BREAK = 0x10,
IRQ_FIFORX_RDY = 0x20,
IRQ_FIFOTX_EMPTY = 0x40,
IRQ_GENERAL_TIMER = 0x80
};
enum
{
TIMER_SYSTEM_CLOCK = 0x200, // CLK input - anywhere from 1MHz up to 4MHz
TIMER_MIDI_CLOCK, // CLKM input - usually either 1MHz or 0.5MHz, or CLKF input - usually 614.4kHz
};
void transmit_clk();
void reset_midi_timer();
void set_comms_mode();
void set_irq(uint8_t irq);
void reset_irq(uint8_t irq);
devcb_write_line m_irq_handler;
devcb_write_line m_txd_handler;
devcb_read_line m_rxd_handler;
emu_timer* m_clock_timer;
emu_timer* m_midi_timer;
std::vector<uint8_t> m_reg;
uint8_t m_wdr;
uint64_t m_prev_rate;
uint8_t m_irq_status;
uint16_t m_general_counter;
uint16_t m_midi_counter;
uint8_t m_vector;
std::queue<uint8_t> m_tx_fifo;
std::queue<uint8_t> m_rx_fifo;
std::queue<uint8_t> m_itx_fifo;
std::queue<uint8_t> m_irx_fifo;
bool m_tx_busy;
uint64_t m_clkm_rate;
uint64_t m_clkf_rate;
};
DECLARE_DEVICE_TYPE(YM3802, ym3802_device)
#endif // DEVICES_MACHINE_YM3802_H

View File

@ -123,9 +123,9 @@
#include "machine/mb89352.h"
#include "machine/nvram.h"
#include "bus/x68k/x68kexp.h"
#include "bus/x68k/x68k_neptunex.h"
#include "bus/x68k/x68k_scsiext.h"
#include "bus/x68k/x68k_midi.h"
#include "bus/scsi/scsi.h"
#include "bus/scsi/scsihd.h"
#include "bus/scsi/scsicd.h"
@ -1474,9 +1474,17 @@ WRITE_LINE_MEMBER(x68k_state::x68k_irq2_line)
}
WRITE_LINE_MEMBER(x68k_state::x68k_irq4_line)
{
m_current_vector[4] = m_expansion->vector();
m_maincpu->set_input_line_and_vector(4,state,m_current_vector[4]);
logerror("EXP: IRQ4 set to %i (vector %02x)\n",state,m_current_vector[4]);
}
static SLOT_INTERFACE_START(x68000_exp_cards)
SLOT_INTERFACE("neptunex",X68K_NEPTUNEX) // Neptune-X ethernet adapter (ISA NE2000 bridge)
SLOT_INTERFACE("cz6bs1",X68K_SCSIEXT) // Sharp CZ-6BS1 SCSI-1 controller
SLOT_INTERFACE("x68k_midi",X68K_MIDI) // X68000 MIDI interface
SLOT_INTERFACE_END
MACHINE_RESET_MEMBER(x68k_state,x68000)
@ -1726,7 +1734,7 @@ static MACHINE_CONFIG_START( x68000 )
MCFG_DEVICE_ADD("exp", X68K_EXPANSION_SLOT, 0)
MCFG_DEVICE_SLOT_INTERFACE(x68000_exp_cards, nullptr, false)
MCFG_X68K_EXPANSION_SLOT_OUT_IRQ2_CB(WRITELINE(x68k_state, x68k_irq2_line))
MCFG_X68K_EXPANSION_SLOT_OUT_IRQ4_CB(INPUTLINE("maincpu", M68K_IRQ_4))
MCFG_X68K_EXPANSION_SLOT_OUT_IRQ4_CB(WRITELINE(x68k_state, x68k_irq4_line))
MCFG_X68K_EXPANSION_SLOT_OUT_NMI_CB(INPUTLINE("maincpu", INPUT_LINE_NMI))
/* internal ram */

View File

@ -21,6 +21,7 @@
#include "machine/upd765.h"
#include "sound/okim6258.h"
#include "sound/ym2151.h"
#include "bus/x68k/x68kexp.h"
#include "screen.h"
@ -67,6 +68,7 @@ public:
m_ppi(*this, "ppi8255"),
m_screen(*this, "screen"),
m_upd72065(*this, "upd72065"),
m_expansion(*this, "exp"),
m_options(*this, "options"),
m_mouse1(*this, "mouse1"),
m_mouse2(*this, "mouse2"),
@ -98,6 +100,7 @@ public:
required_device<i8255_device> m_ppi;
required_device<screen_device> m_screen;
required_device<upd72065_device> m_upd72065;
required_device<x68k_expansion_slot_device> m_expansion;
required_ioport m_options;
required_ioport m_mouse1;
@ -300,6 +303,7 @@ public:
DECLARE_WRITE_LINE_MEMBER(x68k_fm_irq);
DECLARE_WRITE_LINE_MEMBER(x68k_irq2_line);
DECLARE_WRITE_LINE_MEMBER(x68k_irq4_line);
DECLARE_WRITE16_MEMBER(x68k_scc_w);
DECLARE_WRITE16_MEMBER(x68k_fdc_w);