(MESS) SoundBlaster: MIDI Out support for single-byte, SB UART, and MPU-401 UART modes. [R. Belmont]

This commit is contained in:
R. Belmont 2013-01-31 05:47:51 +00:00
parent b03019053e
commit 39f48b0bee
3 changed files with 252 additions and 23 deletions

View File

@ -44,7 +44,7 @@ static const int m_cmd_fifo_length[256] =
-1, -1, -1, -1, 1, 3, -1, -1, -1, -1, -1, -1, -1, -1, 2, 1, /* 0x */
2, -1, -1, -1, 3, -1, 3, 3, -1, -1, -1, -1, 1, -1, -1, 1, /* 1x */
-1, -1, -1, -1, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 2x */
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 3x */
1, 1, -1, -1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, /* 3x */
2, 3, 3, -1, -1, -1, -1, -1, 3, -1, -1, -1, -1, -1, -1, -1, /* 4x */
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 5x */
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 6x */
@ -71,6 +71,15 @@ static const ymf262_interface pc_ymf262_interface =
NULL
};
static SLOT_INTERFACE_START(midiout_slot)
SLOT_INTERFACE("midiout", MIDIOUT_PORT)
SLOT_INTERFACE_END
static const serial_port_interface midiout_intf =
{
DEVCB_NULL // midi out ports don't transmit inward
};
static MACHINE_CONFIG_FRAGMENT( sblaster1_0_config )
MCFG_SPEAKER_STANDARD_STEREO("lspeaker", "rspeaker")
MCFG_SOUND_ADD("ym3812", YM3812, ym3812_StdClock)
@ -90,6 +99,7 @@ static MACHINE_CONFIG_FRAGMENT( sblaster1_0_config )
MCFG_SOUND_ROUTE(ALL_OUTPUTS, "rspeaker", 1.00)
MCFG_PC_JOY_ADD("joy")
MCFG_SERIAL_PORT_ADD("mdout", midiout_intf, midiout_slot, "midiout", NULL)
MACHINE_CONFIG_END
static MACHINE_CONFIG_FRAGMENT( sblaster1_5_config )
@ -106,6 +116,7 @@ static MACHINE_CONFIG_FRAGMENT( sblaster1_5_config )
MCFG_SOUND_ROUTE(ALL_OUTPUTS, "rspeaker", 1.00)
MCFG_PC_JOY_ADD("joy")
MCFG_SERIAL_PORT_ADD("mdout", midiout_intf, midiout_slot, "midiout", NULL)
MACHINE_CONFIG_END
static MACHINE_CONFIG_FRAGMENT( sblaster_16_config )
@ -122,6 +133,7 @@ static MACHINE_CONFIG_FRAGMENT( sblaster_16_config )
MCFG_SOUND_ROUTE(ALL_OUTPUTS, "rspeaker", 1.00)
MCFG_PC_JOY_ADD("joy")
MCFG_SERIAL_PORT_ADD("mdout", midiout_intf, midiout_slot, "midiout", NULL)
MACHINE_CONFIG_END
static READ8_DEVICE_HANDLER( ym3812_16_r )
@ -225,28 +237,41 @@ WRITE8_MEMBER( sb_device::dsp_reset_w )
if(offset)
return;
if(data == 0 && m_dsp.reset_latch == 1)
// a reset while in UART MIDI mode simply restores the previous
// operating state (page 5-3 of the Creative manual).
if (!m_uart_midi)
{
// reset routine
m_dsp.fifo_ptr = 0;
m_dsp.fifo_r_ptr = 0;
for(int i=0;i < 15; i++)
if(data == 0 && m_dsp.reset_latch == 1)
{
m_dsp.fifo[i] = 0;
m_dsp.fifo_r[i] = 0;
// reset routine
m_dsp.fifo_ptr = 0;
m_dsp.fifo_r_ptr = 0;
for(int i=0;i < 15; i++)
{
m_dsp.fifo[i] = 0;
m_dsp.fifo_r[i] = 0;
}
queue_r(0xaa); // reset OK ID
}
queue_r(0xaa); // reset OK ID
m_dsp.reset_latch = data;
drq_w(0);
m_dsp.dma_autoinit = 0;
irq_w(0, IRQ_ALL);
m_timer->adjust(attotime::never, 0);
m_dsp.d_rptr = 0;
m_dsp.d_wptr = 0;
m_dsp.dma_throttled = false;
m_dsp.dma_timer_started = false;
}
m_dsp.reset_latch = data;
drq_w(0);
m_dsp.dma_autoinit = 0;
irq_w(0, IRQ_ALL);
m_timer->adjust(attotime::never, 0);
m_dsp.d_rptr = 0;
m_dsp.d_wptr = 0;
m_dsp.dma_throttled = false;
m_dsp.dma_timer_started = false;
m_onebyte_midi = false;
m_uart_midi = false;
m_uart_irq = false;
m_mpu_midi = false;
m_tx_busy = false;
m_xmit_read = m_xmit_write = 0;
m_recv_read = m_recv_write = 0;
//printf("%02x\n",data);
}
@ -257,6 +282,19 @@ READ8_MEMBER( sb_device::dsp_data_r )
if(offset)
return 0xff;
if (m_uart_midi)
{
UINT8 rv = m_recvring[m_recv_read];
// only advance the read pointer if the ring wasn't empty
if (m_recv_read != m_xmit_read)
{
m_recv_read++;
}
return rv;
}
return dequeue_r();
}
@ -282,6 +320,18 @@ READ8_MEMBER(sb_device::dsp_rbuf_status_r)
// printf("Clear IRQ5\n");
irq_w(0, IRQ_DMA8); // reading this port ACKs the card's IRQ, 8-bit dma only?
// in UART MIDI mode, bit 7 indicates if a character is available
// to read.
if (m_uart_midi)
{
if (m_recv_read != m_recv_write)
{
return 0x80;
}
return 0x00;
}
return m_dsp.rbuf_status;
}
@ -363,6 +413,25 @@ void sb_device::process_fifo(UINT8 cmd)
logerror("SB: ADC capture unimplemented\n");
break;
case 0x34:
m_uart_midi = true;
m_uart_irq = true;
break;
case 0x35:
m_uart_midi = true;
m_uart_irq = false;
break;
case 0x36:
case 0x37: // Enter UART mode
printf("timestamp MIDI mode not supported, contact MESSDEV!\n");
break;
case 0x38: // single-byte MIDI send
m_onebyte_midi = true;
break;
case 0x40: // set time constant
m_dsp.frequency = (1000000 / (256 - m_dsp.fifo[1]));
//printf("Set time constant: %02x -> %d\n", m_dsp.fifo[1], m_dsp.frequency);
@ -609,11 +678,18 @@ void sb_device::process_fifo(UINT8 cmd)
WRITE8_MEMBER(sb_device::dsp_cmd_w)
{
// printf("%02x to DSP command @ %x\n", data, offset);
// printf("%02x to DSP command @ %x\n", data, offset);
if(offset)
return;
if (m_uart_midi || m_onebyte_midi)
{
xmit_char(data);
m_onebyte_midi = false; // clear onebyte (if this is uart, that's harmless)
return;
}
queue(data);
process_fifo(m_dsp.fifo[0]);
@ -698,6 +774,10 @@ WRITE8_MEMBER( sb16_device::mpu401_w )
if(offset == 0) // data
{
logerror("SB MPU401:%02x %02x\n",offset,data);
if (m_mpu_midi)
{
xmit_char(data);
}
}
else // command
{
@ -705,10 +785,18 @@ WRITE8_MEMBER( sb16_device::mpu401_w )
switch(data)
{
case 0x3f: // enter MPU-401 UART mode
irq_w(1, IRQ_MPU);
m_head = m_tail = 0;
m_mpu_queue[m_head++] = 0xfe;
m_mpu_midi = true;
break;
case 0xff: // reset
irq_w(1, IRQ_MPU);
m_head = m_tail = 0;
m_mpu_queue[m_head++] = 0xfe;
m_mpu_midi = false;
break;
}
}
@ -988,9 +1076,11 @@ machine_config_constructor isa16_sblaster16_device::device_mconfig_additions() c
sb_device::sb_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, UINT32 clock, const char *name) :
device_t(mconfig, type, name, tag, owner, clock),
device_serial_interface(mconfig, *this),
m_dacl(*this, "sbdacl"),
m_dacr(*this, "sbdacr"),
m_joy(*this, "joy")
m_joy(*this, "joy"),
m_mdout(*this, "mdout")
{
}
@ -1116,6 +1206,11 @@ void sb_device::device_reset()
m_dsp.irq_active = 0;
m_dsp.dma_no_irq = false;
mixer_reset();
// MIDI is 31250 baud, 8-N-1
set_rcv_rate(31250);
set_tra_rate(31250);
set_data_frame(8, 1, SERIAL_PARITY_NONE);
}
UINT8 sb_device::dack_r(int line)
@ -1406,3 +1501,96 @@ void sb_device::device_timer(emu_timer &timer, device_timer_id tid, int param, v
}
}
}
void sb_device::rcv_complete() // Rx completed receiving byte
{
receive_register_extract();
UINT8 data = get_received_char();
// in UART MIDI mode, we set the DMA8 IRQ on receiving a character
if (m_uart_midi)
{
m_recvring[m_recv_write++] = data;
// if not polling mode, trigger the DMA8 IRQ
if (m_uart_irq)
{
irq_w(1, IRQ_DMA8);
}
}
}
void sb16_device::rcv_complete() // Rx completed receiving byte
{
receive_register_extract();
UINT8 data = get_received_char();
// in UART MIDI mode, we set the DMA8 IRQ on receiving a character
if (m_uart_midi)
{
m_recvring[m_recv_write++] = data;
irq_w(1, IRQ_DMA8);
}
// in MPU MIDI mode, do this instead
if (m_mpu_midi)
{
m_mpu_queue[m_head++] = data;
if (m_head >= 16)
{
m_head = 0;
}
irq_w(1, IRQ_MPU);
}
}
void sb_device::tra_complete() // Tx completed sending byte
{
// printf("Tx complete\n");
// is there more waiting to send?
if (m_xmit_read != m_xmit_write)
{
transmit_register_setup(m_xmitring[m_xmit_read++]);
if (m_xmit_read >= MIDI_RING_SIZE)
{
m_xmit_read = 0;
}
}
else
{
m_tx_busy = false;
}
}
void sb_device::tra_callback() // Tx send bit
{
int bit = transmit_register_get_data_bit();
m_mdout->tx(bit);
}
void sb_device::xmit_char(UINT8 data)
{
// printf("SB: xmit %02x\n", data);
// if tx is busy it'll pick this up automatically when it completes
if (!m_tx_busy)
{
m_tx_busy = true;
transmit_register_setup(data);
}
else
{
// tx is busy, it'll pick this up next time
m_xmitring[m_xmit_write++] = data;
if (m_xmit_write >= MIDI_RING_SIZE)
{
m_xmit_write = 0;
}
if (m_xmit_write == m_xmit_read)
{
printf("Overflow xmitring!\n");
}
}
}

View File

@ -7,6 +7,9 @@
#include "machine/isa.h"
#include "sound/dac.h"
#include "machine/pc_joy.h"
#include "machine/serial.h"
#include "machine/midiinport.h"
#include "machine/midioutport.h"
#define SIXTEENBIT 0x01
#define STEREO 0x02
@ -86,10 +89,10 @@ struct sb16_mixer
UINT8 bass[2];
};
// ======================> sb8_device (parent)
// ======================> sb_device (parent)
class sb_device :
public device_t
public device_t, public device_serial_interface
{
public:
// construction/destruction
@ -97,7 +100,8 @@ public:
required_device<dac_device> m_dacl;
required_device<dac_device> m_dacr;
required_device<pc_joy_device> m_joy;
required_device<pc_joy_device> m_joy;
required_device<serial_port_device> m_mdout;
void process_fifo(UINT8 cmd);
void queue(UINT8 data);
@ -125,8 +129,23 @@ protected:
virtual void mixer_reset() {}
void adpcm_decode(UINT8 sample, int size);
// serial overrides
virtual void rcv_complete(); // Rx completed receiving byte
virtual void tra_complete(); // Tx completed sending byte
virtual void tra_callback(); // Tx send bit
void input_callback(UINT8 state) {}
static const int MIDI_RING_SIZE = 1024;
struct sb8_dsp_state m_dsp;
UINT8 m_dack_out;
void xmit_char(UINT8 data);
bool m_onebyte_midi, m_uart_midi, m_uart_irq, m_mpu_midi;
UINT8 m_recvring[MIDI_RING_SIZE];
UINT8 m_xmitring[MIDI_RING_SIZE];
int m_xmit_read, m_xmit_write;
int m_recv_read, m_recv_write;
bool m_tx_busy;
emu_timer *m_timer;
};
@ -198,6 +217,7 @@ protected:
virtual void irq_w(int state, int source) { (state?m_dsp.irq_active|=source:m_dsp.irq_active&=~source); m_isa->irq5_w(m_dsp.irq_active); }
virtual void mixer_reset();
void mixer_set();
virtual void rcv_complete(); // Rx completed receiving byte
private:
UINT8 m_mpu_queue[16];
UINT8 m_tail;

View File

@ -243,6 +243,20 @@ void osd_write_midi_channel(osd_midi_device *dev, UINT8 data)
#ifndef DISABLE_MIDI
int bytes_needed = 0;
// skip sysex data
if (dev->last_status == 0xf0)
{
if (data != 0xf7)
{
return;
}
else
{
dev->last_status = 0xf7;
return;
}
}
if ((dev->xmit_cnt == 0) && (data & 0x80))
{
dev->last_status = data;
@ -258,6 +272,13 @@ void osd_write_midi_channel(osd_midi_device *dev, UINT8 data)
dev->xmit_in[dev->xmit_cnt++] = data;
}
if ((dev->xmit_cnt == 1) && (dev->xmit_in[0] == 0xf0))
{
dev->xmit_cnt = 0;
dev->last_status = 0xf0;
return;
}
// are we there yet?
switch ((dev->xmit_in[0]>>4) & 0xf)
{