Update for MPU401 by TheCollector1995, fixing NT - pending checking with MPU401 TechRef.

This commit is contained in:
waltje
2017-11-23 21:45:10 -05:00
parent 397c693170
commit d287293a75

View File

@@ -8,7 +8,7 @@
* *
* Roland MPU-401 emulation. * Roland MPU-401 emulation.
* *
* Version: @(#)snd_mpu401.c 1.0.6 2017/11/04 * Version: @(#)snd_mpu401.c 1.0.7 2017/11/23
* *
* Authors: Sarah Walker, <http://pcem-emulator.co.uk/> * Authors: Sarah Walker, <http://pcem-emulator.co.uk/>
* DOSBox Team, * DOSBox Team,
@@ -35,14 +35,11 @@
#include "midi.h" #include "midi.h"
enum enum {
{
STATUS_OUTPUT_NOT_READY = 0x40, STATUS_OUTPUT_NOT_READY = 0x40,
STATUS_INPUT_NOT_READY = 0x80 STATUS_INPUT_NOT_READY = 0x80
}; };
static void MPU401_WriteCommand(mpu_t *mpu, uint8_t val);
static void MPU401_EOIHandlerDispatch(void *p);
int mpu401_standalone_enable = 0; int mpu401_standalone_enable = 0;
@@ -55,6 +52,11 @@ static int mpu401_do_log = ENABLE_MPU401_LOG;
static char logfmt[512]; static char logfmt[512];
#endif #endif
static void MPU401_WriteCommand(mpu_t *mpu, uint8_t val);
static void MPU401_EOIHandlerDispatch(void *p);
static void static void
mpulog(const char *fmt, ...) mpulog(const char *fmt, ...)
{ {
@@ -74,22 +76,19 @@ mpulog(const char *fmt, ...)
#define pclog mpulog #define pclog mpulog
static void QueueByte(mpu_t *mpu, uint8_t data) static void
{ QueueByte(mpu_t *mpu, uint8_t data)
if (mpu->state.block_ack)
{ {
if (mpu->state.block_ack) {
mpu->state.block_ack=0; mpu->state.block_ack=0;
return; return;
} }
if (mpu->queue_used == 0 && mpu->intelligent) if (mpu->queue_used == 0 && mpu->intelligent) {
{
mpu->state.irq_pending=1; mpu->state.irq_pending=1;
//PIC_ActivateIRQ(mpu->irq);
picint(1 << mpu->irq); picint(1 << mpu->irq);
} }
if (mpu->queue_used < MPU401_QUEUE) if (mpu->queue_used < MPU401_QUEUE) {
{
int pos = mpu->queue_used+mpu->queue_pos; int pos = mpu->queue_used+mpu->queue_pos;
if (mpu->queue_pos >= MPU401_QUEUE) if (mpu->queue_pos >= MPU401_QUEUE)
@@ -100,22 +99,26 @@ static void QueueByte(mpu_t *mpu, uint8_t data)
mpu->queue_used++; mpu->queue_used++;
mpu->queue[pos]=data; mpu->queue[pos]=data;
} } else
else
pclog("MPU401:Data queue full\n"); pclog("MPU401:Data queue full\n");
} }
static void ClrQueue(mpu_t *mpu)
static void
ClrQueue(mpu_t *mpu)
{ {
mpu->queue_used=0; mpu->queue_used=0;
mpu->queue_pos=0; mpu->queue_pos=0;
} }
static void MPU401_Reset(mpu_t *mpu)
static void
MPU401_Reset(mpu_t *mpu)
{ {
uint8_t i; uint8_t i;
picintc(1 << mpu->irq); picintc(1 << mpu->irq);
mpu->mode = (mpu->intelligent ? M_INTELLIGENT : M_UART); mpu->mode = (mpu->intelligent ? M_INTELLIGENT : M_UART);
mpu->state.eoi_scheduled = 0; mpu->state.eoi_scheduled = 0;
mpu->state.wsd = 0; mpu->state.wsd = 0;
@@ -139,165 +142,195 @@ static void MPU401_Reset(mpu_t *mpu)
mpu->clock.clock_to_host = 0; mpu->clock.clock_to_host = 0;
mpu->clock.cth_rate = 60; mpu->clock.cth_rate = 60;
mpu->clock.cth_counter = 0; mpu->clock.cth_counter = 0;
ClrQueue(mpu); ClrQueue(mpu);
mpu->state.req_mask = 0; mpu->state.req_mask = 0;
mpu->condbuf.counter = 0; mpu->condbuf.counter = 0;
mpu->condbuf.type = T_OVERFLOW; mpu->condbuf.type = T_OVERFLOW;
for (i=0;i<8;i++) {mpu->playbuf[i].type=T_OVERFLOW;mpu->playbuf[i].counter=0;}
for (i=0;i<8;i++) {
mpu->playbuf[i].type = T_OVERFLOW;
mpu->playbuf[i].counter = 0;
}
} }
static void MPU401_ResetDone(void *p)
static void
MPU401_ResetDone(void *priv)
{ {
mpu_t *mpu = (mpu_t *)p; mpu_t *mpu = (mpu_t *)priv;
pclog("MPU-401 reset callback\n"); pclog("MPU-401 reset callback\n");
mpu401_reset_callback = 0LL; mpu401_reset_callback = 0LL;
mpu->state.reset=0; mpu->state.reset=0;
if (mpu->state.cmd_pending) if (mpu->state.cmd_pending) {
{
MPU401_WriteCommand(mpu, mpu->state.cmd_pending-1); MPU401_WriteCommand(mpu, mpu->state.cmd_pending-1);
mpu->state.cmd_pending=0; mpu->state.cmd_pending=0;
} }
} }
static void MPU401_WriteCommand(mpu_t *mpu, uint8_t val)
static void
MPU401_WriteCommand(mpu_t *mpu, uint8_t val)
{ {
uint8_t i; uint8_t i;
if (mpu->state.reset) if (mpu->state.reset) {
{
mpu->state.cmd_pending=val+1; mpu->state.cmd_pending=val+1;
} }
if (val<=0x2f) if (val <= 0x2f) {
{ switch (val&3) { /* MIDI stop, start, continue */
switch (val&3) case 1:
{ /* MIDI stop, start, continue */ midi_write(0xfc);
case 1: {midi_write(0xfc);break;} break;
case 2: {midi_write(0xfa);break;}
case 3: {midi_write(0xfb);break;} case 2:
midi_write(0xfa);
break;
case 3:
midi_write(0xfb);
break;
} }
// if (val&0x20) LOG(LOG_MISC,LOG_ERROR)("MPU-401:Unhandled Recording Command %x",(int)val);
switch (val&0xc) #if 0
{ if (val&0x20)
pclog("MPU-401:Unhandled Recording Command %x",(int)val);
#endif
switch (val & 0xc) {
case 0x4: /* Stop */ case 0x4: /* Stop */
mpu->state.playing = 0; mpu->state.playing = 0;
mpu401_event_callback = 0LL; mpu401_event_callback = 0LL;
for (i=0xb0;i<0xbf;i++)
{ /* All notes off */ for (i=0xb0; i<0xbf; i++) {
/* All notes off */
midi_write(i); midi_write(i);
midi_write(0x7b); midi_write(0x7b);
midi_write(0); midi_write(0);
} }
break; break;
case 0x8: /* Play */ case 0x8: /* Play */
// LOG(LOG_MISC,LOG_NORMAL)("MPU-401:Intelligent mode playback started"); // pclog("MPU-401:Intelligent mode playback started");
mpu->state.playing = 1; mpu->state.playing = 1;
mpu401_event_callback = (MPU401_TIMECONSTANT / (mpu->clock.tempo*mpu->clock.timebase)) * 1000LL * TIMER_USEC; mpu401_event_callback = (MPU401_TIMECONSTANT / (mpu->clock.tempo*mpu->clock.timebase)) * 1000LL * TIMER_USEC;
ClrQueue(mpu); ClrQueue(mpu);
break; break;
} }
} } else if (val>=0xa0 && val<=0xa7) { /* Request play counter */
else if (val>=0xa0 && val<=0xa7)
{ /* Request play counter */
if (mpu->state.cmask&(1<<(val&7))) QueueByte(mpu, mpu->playbuf[val&7].counter); if (mpu->state.cmask&(1<<(val&7))) QueueByte(mpu, mpu->playbuf[val&7].counter);
} } else if (val>=0xd0 && val<=0xd7) { /* Send data */
else if (val>=0xd0 && val<=0xd7)
{ /* Send data */
mpu->state.old_chan = mpu->state.channel; mpu->state.old_chan = mpu->state.channel;
mpu->state.channel= val & 7; mpu->state.channel= val & 7;
mpu->state.wsd = 1; mpu->state.wsd = 1;
mpu->state.wsm = 0; mpu->state.wsm = 0;
mpu->state.wsd_start = 1; mpu->state.wsd_start = 1;
} } else switch (val) {
else
switch (val)
{
case 0xdf: /* Send system message */ case 0xdf: /* Send system message */
mpu->state.wsd = 0; mpu->state.wsd = 0;
mpu->state.wsm = 1; mpu->state.wsm = 1;
mpu->state.wsd_start = 1; mpu->state.wsd_start = 1;
break; break;
case 0x8e: /* Conductor */ case 0x8e: /* Conductor */
mpu->state.cond_set = 0; mpu->state.cond_set = 0;
break; break;
case 0x8f: case 0x8f:
mpu->state.cond_set = 1; mpu->state.cond_set = 1;
break; break;
case 0x94: /* Clock to host */ case 0x94: /* Clock to host */
mpu->clock.clock_to_host=0; mpu->clock.clock_to_host=0;
break; break;
case 0x95: case 0x95:
mpu->clock.clock_to_host=1; mpu->clock.clock_to_host=1;
break; break;
case 0xc2: /* Internal timebase */ case 0xc2: /* Internal timebase */
mpu->clock.timebase=48; mpu->clock.timebase=48;
break; break;
case 0xc3: case 0xc3:
mpu->clock.timebase=72; mpu->clock.timebase=72;
break; break;
case 0xc4: case 0xc4:
mpu->clock.timebase=96; mpu->clock.timebase=96;
break; break;
case 0xc5: case 0xc5:
mpu->clock.timebase=120; mpu->clock.timebase=120;
break; break;
case 0xc6: case 0xc6:
mpu->clock.timebase=144; mpu->clock.timebase=144;
break; break;
case 0xc7: case 0xc7:
mpu->clock.timebase=168; mpu->clock.timebase=168;
break; break;
case 0xc8: case 0xc8:
mpu->clock.timebase=192; mpu->clock.timebase=192;
break; break;
/* Commands with data byte */ /* Commands with data byte */
case 0xe0: case 0xe1: case 0xe2: case 0xe4: case 0xe6: case 0xe0: case 0xe1: case 0xe2: case 0xe4: case 0xe6:
case 0xe7: case 0xec: case 0xed: case 0xee: case 0xef: case 0xe7: case 0xec: case 0xed: case 0xee: case 0xef:
mpu->state.command_byte=val; mpu->state.command_byte=val;
break; break;
/* Commands 0xa# returning data */ /* Commands 0xa# returning data */
case 0xab: /* Request and clear recording counter */ case 0xab: /* Request and clear recording counter */
QueueByte(mpu, MSG_MPU_ACK); QueueByte(mpu, MSG_MPU_ACK);
QueueByte(mpu, 0); QueueByte(mpu, 0);
return; return;
case 0xac: /* Request version */ case 0xac: /* Request version */
QueueByte(mpu, MSG_MPU_ACK); QueueByte(mpu, MSG_MPU_ACK);
QueueByte(mpu, MPU401_VERSION); QueueByte(mpu, MPU401_VERSION);
return; return;
case 0xad: /* Request revision */ case 0xad: /* Request revision */
QueueByte(mpu, MSG_MPU_ACK); QueueByte(mpu, MSG_MPU_ACK);
QueueByte(mpu, MPU401_REVISION); QueueByte(mpu, MPU401_REVISION);
return; return;
case 0xaf: /* Request tempo */ case 0xaf: /* Request tempo */
QueueByte(mpu, MSG_MPU_ACK); QueueByte(mpu, MSG_MPU_ACK);
QueueByte(mpu, mpu->clock.tempo); QueueByte(mpu, mpu->clock.tempo);
return; return;
case 0xb1: /* Reset relative tempo */ case 0xb1: /* Reset relative tempo */
mpu->clock.tempo_rel=40; mpu->clock.tempo_rel=40;
break; break;
case 0xb9: /* Clear play map */ case 0xb9: /* Clear play map */
case 0xb8: /* Clear play counters */ case 0xb8: /* Clear play counters */
for (i=0xb0;i<0xbf;i++) for (i=0xb0;i<0xbf;i++) {
{ /* All notes off */ /* All notes off */
midi_write(i); midi_write(i);
midi_write(0x7b); midi_write(0x7b);
midi_write(0); midi_write(0);
} }
for (i=0;i<8;i++) for (i=0;i<8;i++) {
{
mpu->playbuf[i].counter=0; mpu->playbuf[i].counter=0;
mpu->playbuf[i].type=T_OVERFLOW; mpu->playbuf[i].type=T_OVERFLOW;
} }
mpu->condbuf.counter=0; mpu->condbuf.counter=0;
mpu->condbuf.type=T_OVERFLOW; mpu->condbuf.type=T_OVERFLOW;
if (!(mpu->state.conductor=mpu->state.cond_set)) mpu->state.cond_req=0; if (!(mpu->state.conductor=mpu->state.cond_set))
mpu->state.cond_req=0;
mpu->state.amask=mpu->state.tmask; mpu->state.amask=mpu->state.tmask;
mpu->state.req_mask=0; mpu->state.req_mask=0;
mpu->state.irq_pending=1; mpu->state.irq_pending=1;
break; break;
case 0xff: /* Reset MPU-401 */ case 0xff: /* Reset MPU-401 */
pclog("MPU-401:Reset %X\n",val); pclog("MPU-401:Reset %X\n",val);
mpu401_reset_callback = MPU401_RESETBUSY * 33LL * TIMER_USEC; mpu401_reset_callback = MPU401_RESETBUSY * 33LL * TIMER_USEC;
@@ -307,55 +340,67 @@ static void MPU401_WriteCommand(mpu_t *mpu, uint8_t val)
if (mpu->mode==M_UART) return;//do not send ack in UART mode if (mpu->mode==M_UART) return;//do not send ack in UART mode
#endif #endif
break; break;
case 0x3f: /* UART mode */ case 0x3f: /* UART mode */
pclog("MPU-401:Set UART mode %X\n",val); pclog("MPU-401:Set UART mode %X\n",val);
mpu->mode=M_UART; mpu->mode=M_UART;
break; break;
default:; default:;
//LOG(LOG_MISC,LOG_NORMAL)("MPU-401:Unhandled command %X",val); //pclog("MPU-401:Unhandled command %X",val);
} }
QueueByte(mpu, MSG_MPU_ACK); QueueByte(mpu, MSG_MPU_ACK);
} }
static void MPU401_WriteData(mpu_t *mpu, uint8_t val)
static void
MPU401_WriteData(mpu_t *mpu, uint8_t val)
{ {
if (mpu->mode==M_UART) {midi_write(val); return;} if (mpu->mode==M_UART) {midi_write(val); return;}
switch (mpu->state.command_byte) switch (mpu->state.command_byte) { /* 0xe# command data */
{ /* 0xe# command data */
case 0x00: case 0x00:
break; break;
case 0xe0: /* Set tempo */ case 0xe0: /* Set tempo */
mpu->state.command_byte=0; mpu->state.command_byte=0;
mpu->clock.tempo=val; mpu->clock.tempo=val;
return; return;
case 0xe1: /* Set relative tempo */ case 0xe1: /* Set relative tempo */
mpu->state.command_byte=0; mpu->state.command_byte=0;
if (val!=0x40) //default value if (val!=0x40) //default value
pclog("MPU-401:Relative tempo change not implemented\n"); pclog("MPU-401:Relative tempo change not implemented\n");
return; return;
case 0xe7: /* Set internal clock to host interval */ case 0xe7: /* Set internal clock to host interval */
mpu->state.command_byte=0; mpu->state.command_byte=0;
mpu->clock.cth_rate=val>>2; mpu->clock.cth_rate=val>>2;
return; return;
case 0xec: /* Set active track mask */ case 0xec: /* Set active track mask */
mpu->state.command_byte=0; mpu->state.command_byte=0;
mpu->state.tmask=val; mpu->state.tmask=val;
return; return;
case 0xed: /* Set play counter mask */ case 0xed: /* Set play counter mask */
mpu->state.command_byte=0; mpu->state.command_byte=0;
mpu->state.cmask=val; mpu->state.cmask=val;
return; return;
case 0xee: /* Set 1-8 MIDI channel mask */ case 0xee: /* Set 1-8 MIDI channel mask */
mpu->state.command_byte=0; mpu->state.command_byte=0;
mpu->state.midi_mask&=0xff00; mpu->state.midi_mask&=0xff00;
mpu->state.midi_mask|=val; mpu->state.midi_mask|=val;
return; return;
case 0xef: /* Set 9-16 MIDI channel mask */ case 0xef: /* Set 9-16 MIDI channel mask */
mpu->state.command_byte=0; mpu->state.command_byte=0;
mpu->state.midi_mask&=0x00ff; mpu->state.midi_mask&=0x00ff;
mpu->state.midi_mask|=((uint16_t)val)<<8; mpu->state.midi_mask|=((uint16_t)val)<<8;
return; return;
//case 0xe2: /* Set graduation for relative tempo */ //case 0xe2: /* Set graduation for relative tempo */
//case 0xe4: /* Set metronome */ //case 0xe4: /* Set metronome */
//case 0xe6: /* Set metronome measure length */ //case 0xe6: /* Set metronome measure length */
@@ -363,11 +408,12 @@ static void MPU401_WriteData(mpu_t *mpu, uint8_t val)
mpu->state.command_byte=0; mpu->state.command_byte=0;
return; return;
} }
static int length,cnt,posd; static int length,cnt,posd;
if (mpu->state.wsd)
{ /* Directly send MIDI message */ if (mpu->state.wsd) {
if (mpu->state.wsd_start) /* Directly send MIDI message */
{ if (mpu->state.wsd_start) {
mpu->state.wsd_start=0; mpu->state.wsd_start=0;
cnt=0; cnt=0;
switch (val&0xf0) { switch (val&0xf0) {
@@ -375,52 +421,77 @@ static void MPU401_WriteData(mpu_t *mpu, uint8_t val)
mpu->playbuf[mpu->state.channel].value[0]=val; mpu->playbuf[mpu->state.channel].value[0]=val;
length=2; length=2;
break; break;
case 0x80:case 0x90:case 0xa0:case 0xb0:case 0xe0: case 0x80:case 0x90:case 0xa0:case 0xb0:case 0xe0:
mpu->playbuf[mpu->state.channel].value[0]=val; mpu->playbuf[mpu->state.channel].value[0]=val;
length=3; length=3;
break; break;
case 0xf0: case 0xf0:
//pclog("MPU-401:Illegal WSD byte\n"); //pclog("MPU-401:Illegal WSD byte\n");
mpu->state.wsd=0; mpu->state.wsd=0;
mpu->state.channel=mpu->state.old_chan; mpu->state.channel=mpu->state.old_chan;
return; return;
default: /* MIDI with running status */ default: /* MIDI with running status */
cnt++; cnt++;
midi_write(mpu->playbuf[mpu->state.channel].value[0]); midi_write(mpu->playbuf[mpu->state.channel].value[0]);
} }
} }
if (cnt<length) {midi_write(val);cnt++;} if (cnt<length) {midi_write(val);cnt++;}
if (cnt==length) { if (cnt==length) {
mpu->state.wsd=0; mpu->state.wsd=0;
mpu->state.channel=mpu->state.old_chan; mpu->state.channel=mpu->state.old_chan;
} }
return; return;
} }
if (mpu->state.wsm)
{ /* Directly send system message */ if (mpu->state.wsm) { /* Directly send system message */
if (val==MSG_EOX) {midi_write(MSG_EOX);mpu->state.wsm=0;return;} if (val==MSG_EOX) {midi_write(MSG_EOX);mpu->state.wsm=0;return;}
if (mpu->state.wsd_start) { if (mpu->state.wsd_start) {
mpu->state.wsd_start=0; mpu->state.wsd_start=0;
cnt=0; cnt=0;
switch (val) switch (val) {
{ case 0xf2:
case 0xf2:{ length=3; break;} length=3;
case 0xf3:{ length=2; break;} break;
case 0xf6:{ length=1; break;}
case 0xf0:{ length=0; break;} case 0xf3:
length=2;
break;
case 0xf6:
length=1;
break;
case 0xf0:
length=0;
break;
default: default:
length=0; length=0;
} }
} }
if (!length || cnt<length) {midi_write(val);cnt++;}
if (!length || cnt<length) {
midi_write(val);
cnt++;
}
if (cnt==length) mpu->state.wsm=0; if (cnt==length) mpu->state.wsm=0;
return; return;
} }
if (mpu->state.cond_req)
{ /* Command */ if (mpu->state.cond_req) {
/* Command */
switch (mpu->state.data_onoff) { switch (mpu->state.data_onoff) {
case -1: case -1:
return; return;
case 0: /* Timing byte */ case 0: /* Timing byte */
mpu->condbuf.vlength=0; mpu->condbuf.vlength=0;
if (val<0xf0) mpu->state.data_onoff++; if (val<0xf0) mpu->state.data_onoff++;
@@ -429,18 +500,22 @@ static void MPU401_WriteData(mpu_t *mpu, uint8_t val)
MPU401_EOIHandlerDispatch(mpu); MPU401_EOIHandlerDispatch(mpu);
return; return;
} }
if (val==0) mpu->state.send_now=1; if (val==0) mpu->state.send_now=1;
else mpu->state.send_now=0; else mpu->state.send_now=0;
mpu->condbuf.counter=val; mpu->condbuf.counter=val;
break; break;
case 1: /* Command byte #1 */ case 1: /* Command byte #1 */
mpu->condbuf.type=T_COMMAND; mpu->condbuf.type=T_COMMAND;
if (val==0xf8 || val==0xf9) mpu->condbuf.type=T_OVERFLOW; if (val==0xf8 || val==0xf9)
mpu->condbuf.type=T_OVERFLOW;
mpu->condbuf.value[mpu->condbuf.vlength]=val; mpu->condbuf.value[mpu->condbuf.vlength]=val;
mpu->condbuf.vlength++; mpu->condbuf.vlength++;
if ((val&0xf0)!=0xe0) MPU401_EOIHandlerDispatch(mpu); if ((val&0xf0)!=0xe0) MPU401_EOIHandlerDispatch(mpu);
else mpu->state.data_onoff++; else mpu->state.data_onoff++;
break; break;
case 2:/* Command byte #2 */ case 2:/* Command byte #2 */
mpu->condbuf.value[mpu->condbuf.vlength]=val; mpu->condbuf.value[mpu->condbuf.vlength]=val;
mpu->condbuf.vlength++; mpu->condbuf.vlength++;
@@ -449,10 +524,12 @@ static void MPU401_WriteData(mpu_t *mpu, uint8_t val)
} }
return; return;
} }
switch (mpu->state.data_onoff)
{ /* Data */ switch (mpu->state.data_onoff) {
/* Data */
case -1: case -1:
return; return;
case 0: /* Timing byte */ case 0: /* Timing byte */
if (val<0xf0) mpu->state.data_onoff=1; if (val<0xf0) mpu->state.data_onoff=1;
else { else {
@@ -464,11 +541,11 @@ static void MPU401_WriteData(mpu_t *mpu, uint8_t val)
else mpu->state.send_now=0; else mpu->state.send_now=0;
mpu->playbuf[mpu->state.channel].counter=val; mpu->playbuf[mpu->state.channel].counter=val;
break; break;
case 1: /* MIDI */ case 1: /* MIDI */
mpu->playbuf[mpu->state.channel].vlength++; mpu->playbuf[mpu->state.channel].vlength++;
posd=mpu->playbuf[mpu->state.channel].vlength; posd=mpu->playbuf[mpu->state.channel].vlength;
if (posd==1) { if (posd==1) switch (val&0xf0) {
switch (val&0xf0) {
case 0xf0: /* System message or mark */ case 0xf0: /* System message or mark */
if (val>0xf7) { if (val>0xf7) {
mpu->playbuf[mpu->state.channel].type=T_MARK; mpu->playbuf[mpu->state.channel].type=T_MARK;
@@ -481,14 +558,17 @@ static void MPU401_WriteData(mpu_t *mpu, uint8_t val)
length=1; length=1;
} }
break; break;
case 0xc0: case 0xd0: /* MIDI Message */ case 0xc0: case 0xd0: /* MIDI Message */
mpu->playbuf[mpu->state.channel].type=T_MIDI_NORM; mpu->playbuf[mpu->state.channel].type=T_MIDI_NORM;
length=mpu->playbuf[mpu->state.channel].length=2; length=mpu->playbuf[mpu->state.channel].length=2;
break; break;
case 0x80: case 0x90: case 0xa0: case 0xb0: case 0xe0: case 0x80: case 0x90: case 0xa0: case 0xb0: case 0xe0:
mpu->playbuf[mpu->state.channel].type=T_MIDI_NORM; mpu->playbuf[mpu->state.channel].type=T_MIDI_NORM;
length=mpu->playbuf[mpu->state.channel].length=3; length=mpu->playbuf[mpu->state.channel].length=3;
break; break;
default: /* MIDI data with running status */ default: /* MIDI data with running status */
posd++; posd++;
mpu->playbuf[mpu->state.channel].vlength++; mpu->playbuf[mpu->state.channel].vlength++;
@@ -496,85 +576,100 @@ static void MPU401_WriteData(mpu_t *mpu, uint8_t val)
length=mpu->playbuf[mpu->state.channel].length; length=mpu->playbuf[mpu->state.channel].length;
break; break;
} }
}
if (!(posd==1 && val>=0xf0)) mpu->playbuf[mpu->state.channel].value[posd-1]=val; if (!(posd==1 && val>=0xf0))
mpu->playbuf[mpu->state.channel].value[posd-1]=val;
if (posd==length) MPU401_EOIHandlerDispatch(mpu); if (posd==length) MPU401_EOIHandlerDispatch(mpu);
} }
} }
static void MPU401_IntelligentOut(mpu_t *mpu, uint8_t chan)
static void
MPU401_IntelligentOut(mpu_t *mpu, uint8_t chan)
{ {
uint8_t val; uint8_t val;
uint8_t i; uint8_t i;
switch (mpu->playbuf[chan].type)
{ switch (mpu->playbuf[chan].type) {
case T_OVERFLOW: case T_OVERFLOW:
break; break;
case T_MARK: case T_MARK:
val=mpu->playbuf[chan].sys_val; val=mpu->playbuf[chan].sys_val;
if (val==0xfc) if (val==0xfc) {
{
midi_write(val); midi_write(val);
mpu->state.amask&=~(1<<chan); mpu->state.amask&=~(1<<chan);
mpu->state.req_mask&=~(1<<chan); mpu->state.req_mask&=~(1<<chan);
} }
break; break;
case T_MIDI_NORM: case T_MIDI_NORM:
for (i=0;i<mpu->playbuf[chan].vlength;i++) for (i=0;i<mpu->playbuf[chan].vlength;i++)
midi_write(mpu->playbuf[chan].value[i]); midi_write(mpu->playbuf[chan].value[i]);
break; break;
default: default:
break; break;
} }
} }
static void UpdateTrack(mpu_t *mpu, uint8_t chan)
static void
UpdateTrack(mpu_t *mpu, uint8_t chan)
{ {
MPU401_IntelligentOut(mpu, chan); MPU401_IntelligentOut(mpu, chan);
if (mpu->state.amask&(1<<chan))
{ if (mpu->state.amask&(1<<chan)) {
mpu->playbuf[chan].vlength=0; mpu->playbuf[chan].vlength=0;
mpu->playbuf[chan].type=T_OVERFLOW; mpu->playbuf[chan].type=T_OVERFLOW;
mpu->playbuf[chan].counter=0xf0; mpu->playbuf[chan].counter=0xf0;
mpu->state.req_mask|=(1<<chan); mpu->state.req_mask|=(1<<chan);
} else { } else {
if (mpu->state.amask==0 && !mpu->state.conductor) mpu->state.req_mask|=(1<<12); if (mpu->state.amask==0 && !mpu->state.conductor)
mpu->state.req_mask|=(1<<12);
} }
} }
static void UpdateConductor(mpu_t *mpu)
{ static void
if (mpu->condbuf.value[0]==0xfc) UpdateConductor(mpu_t *mpu)
{ {
if (mpu->condbuf.value[0]==0xfc) {
mpu->condbuf.value[0]=0; mpu->condbuf.value[0]=0;
mpu->state.conductor=0; mpu->state.conductor=0;
mpu->state.req_mask&=~(1<<9); mpu->state.req_mask&=~(1<<9);
if (mpu->state.amask==0) mpu->state.req_mask|=(1<<12); if (mpu->state.amask==0)
mpu->state.req_mask|=(1<<12);
return; return;
} }
mpu->condbuf.vlength=0; mpu->condbuf.vlength=0;
mpu->condbuf.counter=0xf0; mpu->condbuf.counter=0xf0;
mpu->state.req_mask|=(1<<9); mpu->state.req_mask|=(1<<9);
} }
//Updates counters and requests new data on "End of Input" //Updates counters and requests new data on "End of Input"
static void MPU401_EOIHandler(void *p) static void
MPU401_EOIHandler(void *priv)
{ {
mpu_t *mpu = (mpu_t *)p; mpu_t *mpu = (mpu_t *)priv;
uint8_t i; uint8_t i;
pclog("MPU-401 end of input callback\n"); pclog("MPU-401 end of input callback\n");
mpu401_eoi_callback = 0LL; mpu401_eoi_callback = 0LL;
mpu->state.eoi_scheduled=0; mpu->state.eoi_scheduled=0;
if (mpu->state.send_now) if (mpu->state.send_now) {
{
mpu->state.send_now=0; mpu->state.send_now=0;
if (mpu->state.cond_req) UpdateConductor(mpu); if (mpu->state.cond_req) UpdateConductor(mpu);
else UpdateTrack(mpu, mpu->state.channel); else UpdateTrack(mpu, mpu->state.channel);
} }
mpu->state.irq_pending=0; mpu->state.irq_pending=0;
if (!mpu->state.playing || !mpu->state.req_mask) return; if (!mpu->state.playing || !mpu->state.req_mask) return;
i=0; i=0;
do { do {
if (mpu->state.req_mask&(1<<i)) { if (mpu->state.req_mask&(1<<i)) {
@@ -585,73 +680,78 @@ static void MPU401_EOIHandler(void *p)
} while ((i++)<16); } while ((i++)<16);
} }
static void MPU401_EOIHandlerDispatch(void *p)
{
mpu_t *mpu = (mpu_t *)p;
if (mpu->state.send_now) static void
MPU401_EOIHandlerDispatch(void *priv)
{ {
mpu_t *mpu = (mpu_t *)priv;
if (mpu->state.send_now) {
mpu->state.eoi_scheduled=1; mpu->state.eoi_scheduled=1;
mpu401_eoi_callback = 60LL * TIMER_USEC; /* Possible a bit longer */ mpu401_eoi_callback = 60LL * TIMER_USEC; /* Possible a bit longer */
} } else if (!mpu->state.eoi_scheduled)
else if (!mpu->state.eoi_scheduled)
MPU401_EOIHandler(mpu); MPU401_EOIHandler(mpu);
} }
static void imf_write(uint16_t addr, uint8_t val, void *p)
static void
imf_write(uint16_t addr, uint8_t val, void *priv)
{ {
pclog("IMF:Wr %4X,%X\n", addr, val); pclog("IMF:Wr %4X,%X\n", addr, val);
} }
uint8_t MPU401_ReadData(mpu_t *mpu)
uint8_t
MPU401_ReadData(mpu_t *mpu)
{ {
uint8_t ret; uint8_t ret;
ret = MSG_MPU_ACK; ret = MSG_MPU_ACK;
if (mpu->queue_used) if (mpu->queue_used) {
{
if (mpu->queue_pos>=MPU401_QUEUE) mpu->queue_pos-=MPU401_QUEUE; if (mpu->queue_pos>=MPU401_QUEUE) mpu->queue_pos-=MPU401_QUEUE;
ret=mpu->queue[mpu->queue_pos]; ret=mpu->queue[mpu->queue_pos];
mpu->queue_pos++;mpu->queue_used--; mpu->queue_pos++;mpu->queue_used--;
} }
if (!mpu->intelligent) return ret; if (!mpu->intelligent) return ret;
if (mpu->queue_used == 0) picintc(1 << mpu->irq); if (mpu->queue_used == 0) picintc(1 << mpu->irq);
if (ret>=0xf0 && ret<=0xf7) if (ret>=0xf0 && ret<=0xf7) {
{ /* MIDI data request */ /* MIDI data request */
mpu->state.channel=ret&7; mpu->state.channel=ret&7;
mpu->state.data_onoff=0; mpu->state.data_onoff=0;
mpu->state.cond_req=0; mpu->state.cond_req=0;
} }
if (ret==MSG_MPU_COMMAND_REQ)
{ if (ret==MSG_MPU_COMMAND_REQ) {
mpu->state.data_onoff=0; mpu->state.data_onoff=0;
mpu->state.cond_req=1; mpu->state.cond_req=1;
if (mpu->condbuf.type!=T_OVERFLOW) if (mpu->condbuf.type!=T_OVERFLOW) {
{
mpu->state.block_ack=1; mpu->state.block_ack=1;
MPU401_WriteCommand(mpu, mpu->condbuf.value[0]); MPU401_WriteCommand(mpu, mpu->condbuf.value[0]);
if (mpu->state.command_byte) MPU401_WriteData(mpu, mpu->condbuf.value[1]); if (mpu->state.command_byte)
MPU401_WriteData(mpu, mpu->condbuf.value[1]);
} }
mpu->condbuf.type=T_OVERFLOW; mpu->condbuf.type=T_OVERFLOW;
} }
if (ret==MSG_MPU_END || ret==MSG_MPU_CLOCK || ret==MSG_MPU_ACK) { if (ret==MSG_MPU_END || ret==MSG_MPU_CLOCK || ret==MSG_MPU_ACK) {
mpu->state.data_onoff=-1; mpu->state.data_onoff=-1;
MPU401_EOIHandlerDispatch(mpu); MPU401_EOIHandlerDispatch(mpu);
} }
return ret; return(ret);
} }
static void mpu401_write(uint16_t addr, uint8_t val, void *p)
static void
mpu401_write(uint16_t addr, uint8_t val, void *priv)
{ {
mpu_t *mpu = (mpu_t *)p; mpu_t *mpu = (mpu_t *)priv;
/* pclog("MPU401 Write Port %04X, val %x\n", addr, val); */ /* pclog("MPU401 Write Port %04X, val %x\n", addr, val); */
switch (addr & 1) {
switch (addr & 1)
{
case 0: /*Data*/ case 0: /*Data*/
MPU401_WriteData(mpu, val); MPU401_WriteData(mpu, val);
pclog("Write Data (0x330) %X\n", val); pclog("Write Data (0x330) %X\n", val);
@@ -664,13 +764,14 @@ static void mpu401_write(uint16_t addr, uint8_t val, void *p)
} }
} }
static uint8_t mpu401_read(uint16_t addr, void *p)
static uint8_t
mpu401_read(uint16_t addr, void *priv)
{ {
mpu_t *mpu = (mpu_t *)p; mpu_t *mpu = (mpu_t *)priv;
uint8_t ret = 0; uint8_t ret = 0;
switch (addr & 1) switch (addr & 1) {
{
case 0: //Read Data case 0: //Read Data
ret = MPU401_ReadData(mpu); ret = MPU401_ReadData(mpu);
pclog("Read Data (0x330) %X\n", ret); pclog("Read Data (0x330) %X\n", ret);
@@ -679,38 +780,45 @@ static uint8_t mpu401_read(uint16_t addr, void *p)
case 1: //Read Status case 1: //Read Status
if (mpu->state.cmd_pending) ret=STATUS_OUTPUT_NOT_READY; if (mpu->state.cmd_pending) ret=STATUS_OUTPUT_NOT_READY;
if (!mpu->queue_used) ret=STATUS_INPUT_NOT_READY; if (!mpu->queue_used) ret=STATUS_INPUT_NOT_READY;
ret |= 0x3f; //FIXME: check with MPU401 TechRef
pclog("Read Status (0x331) %x\n", ret); pclog("Read Status (0x331) %x\n", ret);
break; break;
} }
/* pclog("MPU401 Read Port %04X, ret %x\n", addr, ret); */ /* pclog("MPU401 Read Port %04X, ret %x\n", addr, ret); */
return ret; return(ret);
} }
static void MPU401_Event(void *p) static void
MPU401_Event(void *priv)
{ {
mpu_t *mpu = (mpu_t *)p; mpu_t *mpu = (mpu_t *)priv;
uint8_t i;
int new_time; int new_time;
uint8_t i;
pclog("MPU-401 event callback\n"); pclog("MPU-401 event callback\n");
if (mpu->mode==M_UART) if (mpu->mode==M_UART) {
{
mpu401_event_callback = 0LL; mpu401_event_callback = 0LL;
return; return;
} }
if (mpu->state.irq_pending) goto next_event; if (mpu->state.irq_pending) goto next_event;
for (i=0;i<8;i++) { /* Decrease counters */ for (i=0;i<8;i++) { /* Decrease counters */
if (mpu->state.amask&(1<<i)) { if (mpu->state.amask&(1<<i)) {
mpu->playbuf[i].counter--; mpu->playbuf[i].counter--;
if (mpu->playbuf[i].counter<=0) UpdateTrack(mpu, i); if (mpu->playbuf[i].counter<=0) UpdateTrack(mpu, i);
} }
} }
if (mpu->state.conductor) { if (mpu->state.conductor) {
mpu->condbuf.counter--; mpu->condbuf.counter--;
if (mpu->condbuf.counter<=0) UpdateConductor(mpu); if (mpu->condbuf.counter<=0) UpdateConductor(mpu);
} }
if (mpu->clock.clock_to_host) { if (mpu->clock.clock_to_host) {
mpu->clock.cth_counter++; mpu->clock.cth_counter++;
if (mpu->clock.cth_counter >= mpu->clock.cth_rate) { if (mpu->clock.cth_counter >= mpu->clock.cth_rate) {
@@ -718,27 +826,28 @@ static void MPU401_Event(void *p)
mpu->state.req_mask|=(1<<13); mpu->state.req_mask|=(1<<13);
} }
} }
if (!mpu->state.irq_pending && mpu->state.req_mask) MPU401_EOIHandler(mpu);
if (!mpu->state.irq_pending && mpu->state.req_mask)
MPU401_EOIHandler(mpu);
next_event: next_event:
/* mpu401_event_callback = 0LL; */ /* mpu401_event_callback = 0LL; */
new_time = (mpu->clock.tempo * mpu->clock.timebase); new_time = (mpu->clock.tempo * mpu->clock.timebase);
if (new_time == 0) if (new_time == 0) {
{
mpu401_event_callback = 0LL; mpu401_event_callback = 0LL;
return; return;
} } else {
else
{
mpu401_event_callback += (MPU401_TIMECONSTANT/new_time) * 1000LL * TIMER_USEC; mpu401_event_callback += (MPU401_TIMECONSTANT/new_time) * 1000LL * TIMER_USEC;
pclog("Next event after %i us (time constant: %i)\n", (int) ((MPU401_TIMECONSTANT/new_time) * 1000 * TIMER_USEC), (int) MPU401_TIMECONSTANT); pclog("Next event after %i us (time constant: %i)\n", (int) ((MPU401_TIMECONSTANT/new_time) * 1000 * TIMER_USEC), (int) MPU401_TIMECONSTANT);
} }
} }
void mpu401_init(mpu_t *mpu, uint16_t addr, int irq, int mode)
void
mpu401_init(mpu_t *mpu, uint16_t addr, int irq, int mode)
{ {
#if 0 #if 0
if (mode != M_INTELLIGENT) if (mode != M_INTELLIGENT) {
{
mpu401_uart_init(mpu, addr); mpu401_uart_init(mpu, addr);
return; return;
} }
@@ -757,8 +866,10 @@ void mpu401_init(mpu_t *mpu, uint16_t addr, int irq, int mode)
mpu401_eoi_callback = 0LL; mpu401_eoi_callback = 0LL;
mpu401_reset_callback = 0LL; mpu401_reset_callback = 0LL;
io_sethandler(addr, 0x0002, mpu401_read, NULL, NULL, mpu401_write, NULL, NULL, mpu); io_sethandler(addr, 2,
io_sethandler(0x2A20, 0x0010, NULL, NULL, NULL, imf_write, NULL, NULL, mpu); mpu401_read, NULL, NULL, mpu401_write, NULL, NULL, mpu);
io_sethandler(0x2A20, 16,
NULL, NULL, NULL, imf_write, NULL, NULL, mpu);
timer_add(MPU401_Event, &mpu401_event_callback, &mpu401_event_callback, mpu); timer_add(MPU401_Event, &mpu401_event_callback, &mpu401_event_callback, mpu);
timer_add(MPU401_EOIHandler, &mpu401_eoi_callback, &mpu401_eoi_callback, mpu); timer_add(MPU401_EOIHandler, &mpu401_eoi_callback, &mpu401_eoi_callback, mpu);
timer_add(MPU401_ResetDone, &mpu401_reset_callback, &mpu401_reset_callback, mpu); timer_add(MPU401_ResetDone, &mpu401_reset_callback, &mpu401_reset_callback, mpu);
@@ -766,28 +877,25 @@ void mpu401_init(mpu_t *mpu, uint16_t addr, int irq, int mode)
MPU401_Reset(mpu); MPU401_Reset(mpu);
} }
void mpu401_device_add(void)
void
mpu401_device_add(void)
{ {
char *n; char *n;
if (!mpu401_standalone_enable) if (!mpu401_standalone_enable) return;
{
return;
}
n = sound_card_get_internal_name(sound_card_current); n = sound_card_get_internal_name(sound_card_current);
if (n != NULL) if (n != NULL) {
{ if (!strcmp(n, "sb16") || !strcmp(n, "sbawe32")) return;
if (!strcmp(n, "sb16") || !strcmp(n, "sbawe32"))
{
return;
}
} }
device_add(&mpu401_device); device_add(&mpu401_device);
} }
void *mpu401_standalone_init(device_t *info)
static void *
mpu401_standalone_init(device_t *info)
{ {
mpu_t *mpu; mpu_t *mpu;
@@ -797,16 +905,19 @@ void *mpu401_standalone_init(device_t *info)
pclog("mpu_init\n"); pclog("mpu_init\n");
mpu401_init(mpu, device_get_config_hex16("base"), device_get_config_int("irq"), device_get_config_int("mode")); mpu401_init(mpu, device_get_config_hex16("base"), device_get_config_int("irq"), device_get_config_int("mode"));
return mpu; return(mpu);
} }
void mpu401_standalone_close(void *p)
static void
mpu401_standalone_close(void *priv)
{ {
mpu_t *mpu = (mpu_t *)p; mpu_t *mpu = (mpu_t *)priv;
free(mpu); free(mpu);
} }
static device_config_t mpu401_standalone_config[] = static device_config_t mpu401_standalone_config[] =
{ {
{ {
@@ -868,13 +979,13 @@ static device_config_t mpu401_standalone_config[] =
} }
}; };
device_t mpu401_device =
{ device_t mpu401_device = {
"MPU-401 (Standalone)", "MPU-401 (Standalone)",
0, 0, 0, 0,
mpu401_standalone_init, mpu401_standalone_init, mpu401_standalone_close, NULL,
mpu401_standalone_close, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL,
NULL, NULL,
mpu401_standalone_config mpu401_standalone_config