diff options
author | 2017-06-21 18:34:36 +1200 | |
---|---|---|
committer | 2017-06-21 18:37:23 +1200 | |
commit | 03f21234b8e8cfbffa8f0199696307246122de31 (patch) | |
tree | 67d0a775958c287c8debc37c944b5e4984b35c45 /src/devices/machine/ym3802.cpp | |
parent | e6f86fa6738ae3d108da1e0d28e3b09ceb9cc989 (diff) |
- Add preliminary Yamaha YM3802 MIDI controller
- Add X68000 expansion device using the YM3802
Diffstat (limited to 'src/devices/machine/ym3802.cpp')
-rw-r--r-- | src/devices/machine/ym3802.cpp | 286 |
1 files changed, 286 insertions, 0 deletions
diff --git a/src/devices/machine/ym3802.cpp b/src/devices/machine/ym3802.cpp new file mode 100644 index 00000000000..6afd4ea9a12 --- /dev/null +++ b/src/devices/machine/ym3802.cpp @@ -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; + } + } +} |