Forgot to include the new file names.
This commit is contained in:
647
src/SOUND/snd_mpu401.c
Normal file
647
src/SOUND/snd_mpu401.c
Normal file
@@ -0,0 +1,647 @@
|
||||
#include "../ibm.h"
|
||||
#include "../io.h"
|
||||
#include "../pic.h"
|
||||
#include "../timer.h"
|
||||
#include "../plat-midi.h"
|
||||
#include "snd_mpu401.h"
|
||||
|
||||
enum
|
||||
{
|
||||
STATUS_OUTPUT_NOT_READY = 0x40,
|
||||
STATUS_INPUT_NOT_READY = 0x80
|
||||
};
|
||||
|
||||
static void MPU401_WriteCommand(mpu_t *mpu, uint8_t val);
|
||||
static void MPU401_EOIHandlerDispatch(void *p);
|
||||
|
||||
static int mpu401_event_callback = 0;
|
||||
|
||||
static void QueueByte(mpu_t *mpu, uint8_t data)
|
||||
{
|
||||
if (mpu->state.block_ack)
|
||||
{
|
||||
mpu->state.block_ack=0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (mpu->queue_used == 0 && mpu->intelligent)
|
||||
{
|
||||
mpu->state.irq_pending=1;
|
||||
//PIC_ActivateIRQ(mpu->irq);
|
||||
picint(1 << mpu->irq);
|
||||
}
|
||||
if (mpu->queue_used < MPU401_QUEUE)
|
||||
{
|
||||
int pos = mpu->queue_used+mpu->queue_pos;
|
||||
|
||||
if (mpu->queue_pos >= MPU401_QUEUE)
|
||||
mpu->queue_pos -= MPU401_QUEUE;
|
||||
|
||||
if (pos>=MPU401_QUEUE)
|
||||
pos-=MPU401_QUEUE;
|
||||
|
||||
mpu->queue_used++;
|
||||
mpu->queue[pos]=data;
|
||||
}
|
||||
else
|
||||
pclog("MPU401:Data queue full\n");
|
||||
}
|
||||
|
||||
static void ClrQueue(mpu_t *mpu)
|
||||
{
|
||||
mpu->queue_used=0;
|
||||
mpu->queue_pos=0;
|
||||
}
|
||||
|
||||
static void MPU401_Reset(mpu_t *mpu)
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
picintc(1 << mpu->irq);
|
||||
mpu->mode=(mpu->intelligent ? M_INTELLIGENT : M_UART);
|
||||
mpu->state.eoi_scheduled=0;
|
||||
mpu->state.wsd=0;
|
||||
mpu->state.wsm=0;
|
||||
mpu->state.conductor=0;
|
||||
mpu->state.cond_req=0;
|
||||
mpu->state.cond_set=0;
|
||||
mpu->state.playing=0;
|
||||
mpu->state.run_irq=0;
|
||||
mpu->state.irq_pending=0;
|
||||
mpu->state.cmask=0xff;
|
||||
mpu->state.amask=mpu->state.tmask=0;
|
||||
mpu->state.midi_mask=0xffff;
|
||||
mpu->state.data_onoff=0;
|
||||
mpu->state.command_byte=0;
|
||||
mpu->state.block_ack=0;
|
||||
mpu->clock.tempo=mpu->clock.old_tempo=100;
|
||||
mpu->clock.timebase=mpu->clock.old_timebase=120;
|
||||
mpu->clock.tempo_rel=mpu->clock.old_tempo_rel=40;
|
||||
mpu->clock.tempo_grad=0;
|
||||
mpu->clock.clock_to_host=0;
|
||||
mpu->clock.cth_rate=60;
|
||||
mpu->clock.cth_counter=0;
|
||||
ClrQueue(mpu);
|
||||
mpu->state.req_mask=0;
|
||||
mpu->condbuf.counter=0;
|
||||
mpu->condbuf.type=T_OVERFLOW;
|
||||
for (i=0;i<8;i++) {mpu->playbuf[i].type=T_OVERFLOW;mpu->playbuf[i].counter=0;}
|
||||
}
|
||||
|
||||
static void MPU401_ResetDone(mpu_t *mpu, int reset)
|
||||
{
|
||||
mpu->state.reset=0;
|
||||
if (mpu->state.cmd_pending)
|
||||
{
|
||||
MPU401_WriteCommand(mpu, mpu->state.cmd_pending-1);
|
||||
mpu->state.cmd_pending=0;
|
||||
}
|
||||
}
|
||||
|
||||
static void MPU401_WriteCommand(mpu_t *mpu, uint8_t val)
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
if (mpu->state.reset)
|
||||
{
|
||||
mpu->state.cmd_pending=val+1;
|
||||
return;
|
||||
}
|
||||
|
||||
if (val<=0x2f)
|
||||
{
|
||||
switch (val&3)
|
||||
{ /* MIDI stop, start, continue */
|
||||
case 1: {midi_write(0xfc);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)
|
||||
{
|
||||
case 0x4: /* Stop */
|
||||
mpu->state.playing=0;
|
||||
for (i=0xb0;i<0xbf;i++)
|
||||
{ /* All notes off */
|
||||
midi_write(i);
|
||||
midi_write(0x7b);
|
||||
midi_write(0);
|
||||
}
|
||||
break;
|
||||
case 0x8: /* Play */
|
||||
// LOG(LOG_MISC,LOG_NORMAL)("MPU-401:Intelligent mode playback started");
|
||||
mpu->state.playing=1;
|
||||
mpu401_event_callback = MPU401_TIMECONSTANT / (mpu->clock.tempo*mpu->clock.timebase);
|
||||
ClrQueue(mpu);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (val>=0xa0 && val<=0xa7)
|
||||
{ /* Request play counter */
|
||||
if (mpu->state.cmask&(1<<(val&7))) QueueByte(mpu, mpu->playbuf[val&7].counter);
|
||||
}
|
||||
else if (val>=0xd0 && val<=0xd7)
|
||||
{ /* Send data */
|
||||
mpu->state.old_chan=mpu->state.channel;
|
||||
mpu->state.channel=val&7;
|
||||
mpu->state.wsd=1;
|
||||
mpu->state.wsm=0;
|
||||
mpu->state.wsd_start=1;
|
||||
}
|
||||
else
|
||||
switch (val)
|
||||
{
|
||||
case 0xdf: /* Send system message */
|
||||
mpu->state.wsd=0;
|
||||
mpu->state.wsm=1;
|
||||
mpu->state.wsd_start=1;
|
||||
break;
|
||||
case 0x8e: /* Conductor */
|
||||
mpu->state.cond_set=0;
|
||||
break;
|
||||
case 0x8f:
|
||||
mpu->state.cond_set=1;
|
||||
break;
|
||||
case 0x94: /* Clock to host */
|
||||
mpu->clock.clock_to_host=0;
|
||||
break;
|
||||
case 0x95:
|
||||
mpu->clock.clock_to_host=1;
|
||||
break;
|
||||
case 0xc2: /* Internal timebase */
|
||||
mpu->clock.timebase=48;
|
||||
break;
|
||||
case 0xc3:
|
||||
mpu->clock.timebase=72;
|
||||
break;
|
||||
case 0xc4:
|
||||
mpu->clock.timebase=96;
|
||||
break;
|
||||
case 0xc5:
|
||||
mpu->clock.timebase=120;
|
||||
break;
|
||||
case 0xc6:
|
||||
mpu->clock.timebase=144;
|
||||
break;
|
||||
case 0xc7:
|
||||
mpu->clock.timebase=168;
|
||||
break;
|
||||
case 0xc8:
|
||||
mpu->clock.timebase=192;
|
||||
break;
|
||||
/* Commands with data byte */
|
||||
case 0xe0: case 0xe1: case 0xe2: case 0xe4: case 0xe6:
|
||||
case 0xe7: case 0xec: case 0xed: case 0xee: case 0xef:
|
||||
mpu->state.command_byte=val;
|
||||
break;
|
||||
/* Commands 0xa# returning data */
|
||||
case 0xab: /* Request and clear recording counter */
|
||||
QueueByte(mpu, MSG_MPU_ACK);
|
||||
QueueByte(mpu, 0);
|
||||
return;
|
||||
case 0xac: /* Request version */
|
||||
QueueByte(mpu, MSG_MPU_ACK);
|
||||
QueueByte(mpu, MPU401_VERSION);
|
||||
return;
|
||||
case 0xad: /* Request revision */
|
||||
QueueByte(mpu, MSG_MPU_ACK);
|
||||
QueueByte(mpu, MPU401_REVISION);
|
||||
return;
|
||||
case 0xaf: /* Request tempo */
|
||||
QueueByte(mpu, MSG_MPU_ACK);
|
||||
QueueByte(mpu, mpu->clock.tempo);
|
||||
return;
|
||||
case 0xb1: /* Reset relative tempo */
|
||||
mpu->clock.tempo_rel=40;
|
||||
break;
|
||||
case 0xb9: /* Clear play map */
|
||||
case 0xb8: /* Clear play counters */
|
||||
for (i=0xb0;i<0xbf;i++)
|
||||
{ /* All notes off */
|
||||
midi_write(i);
|
||||
midi_write(0x7b);
|
||||
midi_write(0);
|
||||
}
|
||||
for (i=0;i<8;i++)
|
||||
{
|
||||
mpu->playbuf[i].counter=0;
|
||||
mpu->playbuf[i].type=T_OVERFLOW;
|
||||
}
|
||||
mpu->condbuf.counter=0;
|
||||
mpu->condbuf.type=T_OVERFLOW;
|
||||
if (!(mpu->state.conductor=mpu->state.cond_set)) mpu->state.cond_req=0;
|
||||
mpu->state.amask=mpu->state.tmask;
|
||||
mpu->state.req_mask=0;
|
||||
mpu->state.irq_pending=1;
|
||||
break;
|
||||
case 0xff: /* Reset MPU-401 */
|
||||
pclog("MPU-401:Reset %X\n",val);
|
||||
MPU401_ResetDone(mpu, MPU401_RESETBUSY);
|
||||
mpu->state.reset=1;
|
||||
MPU401_Reset(mpu);
|
||||
if (mpu->mode==M_UART) return;//do not send ack in UART mode
|
||||
break;
|
||||
case 0x3f: /* UART mode */
|
||||
pclog("MPU-401:Set UART mode %X\n",val);
|
||||
mpu->mode=M_UART;
|
||||
break;
|
||||
default:;
|
||||
//LOG(LOG_MISC,LOG_NORMAL)("MPU-401:Unhandled command %X",val);
|
||||
}
|
||||
QueueByte(mpu, MSG_MPU_ACK);
|
||||
}
|
||||
|
||||
static void MPU401_WriteData(mpu_t *mpu, uint8_t val)
|
||||
{
|
||||
if (mpu->mode==M_UART) {midi_write(val);}
|
||||
|
||||
switch (mpu->state.command_byte)
|
||||
{ /* 0xe# command data */
|
||||
case 0x00:
|
||||
break;
|
||||
case 0xe0: /* Set tempo */
|
||||
mpu->state.command_byte=0;
|
||||
mpu->clock.tempo=val;
|
||||
return;
|
||||
case 0xe1: /* Set relative tempo */
|
||||
mpu->state.command_byte=0;
|
||||
if (val!=0x40) //default value
|
||||
pclog("MPU-401:Relative tempo change not implemented\n");
|
||||
return;
|
||||
case 0xe7: /* Set internal clock to host interval */
|
||||
mpu->state.command_byte=0;
|
||||
mpu->clock.cth_rate=val>>2;
|
||||
return;
|
||||
case 0xec: /* Set active track mask */
|
||||
mpu->state.command_byte=0;
|
||||
mpu->state.tmask=val;
|
||||
return;
|
||||
case 0xed: /* Set play counter mask */
|
||||
mpu->state.command_byte=0;
|
||||
mpu->state.cmask=val;
|
||||
return;
|
||||
case 0xee: /* Set 1-8 MIDI channel mask */
|
||||
mpu->state.command_byte=0;
|
||||
mpu->state.midi_mask&=0xff00;
|
||||
mpu->state.midi_mask|=val;
|
||||
return;
|
||||
case 0xef: /* Set 9-16 MIDI channel mask */
|
||||
mpu->state.command_byte=0;
|
||||
mpu->state.midi_mask&=0x00ff;
|
||||
mpu->state.midi_mask|=((uint16_t)val)<<8;
|
||||
return;
|
||||
//case 0xe2: /* Set graduation for relative tempo */
|
||||
//case 0xe4: /* Set metronome */
|
||||
//case 0xe6: /* Set metronome measure length */
|
||||
default:
|
||||
mpu->state.command_byte=0;
|
||||
return;
|
||||
}
|
||||
static int length,cnt,posd;
|
||||
if (mpu->state.wsd)
|
||||
{ /* Directly send MIDI message */
|
||||
if (mpu->state.wsd_start)
|
||||
{
|
||||
mpu->state.wsd_start=0;
|
||||
cnt=0;
|
||||
switch (val&0xf0) {
|
||||
case 0xc0:case 0xd0:
|
||||
mpu->playbuf[mpu->state.channel].value[0]=val;
|
||||
length=2;
|
||||
break;
|
||||
case 0x80:case 0x90:case 0xa0:case 0xb0:case 0xe0:
|
||||
mpu->playbuf[mpu->state.channel].value[0]=val;
|
||||
length=3;
|
||||
break;
|
||||
case 0xf0:
|
||||
//pclog("MPU-401:Illegal WSD byte\n");
|
||||
mpu->state.wsd=0;
|
||||
mpu->state.channel=mpu->state.old_chan;
|
||||
return;
|
||||
default: /* MIDI with running status */
|
||||
cnt++;
|
||||
midi_write(mpu->playbuf[mpu->state.channel].value[0]);
|
||||
}
|
||||
}
|
||||
if (cnt<length) {midi_write(val);cnt++;}
|
||||
if (cnt==length) {
|
||||
mpu->state.wsd=0;
|
||||
mpu->state.channel=mpu->state.old_chan;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (mpu->state.wsm)
|
||||
{ /* Directly send system message */
|
||||
if (val==MSG_EOX) {midi_write(MSG_EOX);mpu->state.wsm=0;return;}
|
||||
if (mpu->state.wsd_start) {
|
||||
mpu->state.wsd_start=0;
|
||||
cnt=0;
|
||||
switch (val)
|
||||
{
|
||||
case 0xf2:{ length=3; break;}
|
||||
case 0xf3:{ length=2; break;}
|
||||
case 0xf6:{ length=1; break;}
|
||||
case 0xf0:{ length=0; break;}
|
||||
default:
|
||||
length=0;
|
||||
}
|
||||
}
|
||||
if (!length || cnt<length) {midi_write(val);cnt++;}
|
||||
if (cnt==length) mpu->state.wsm=0;
|
||||
return;
|
||||
}
|
||||
if (mpu->state.cond_req)
|
||||
{ /* Command */
|
||||
switch (mpu->state.data_onoff) {
|
||||
case -1:
|
||||
return;
|
||||
case 0: /* Timing byte */
|
||||
mpu->condbuf.vlength=0;
|
||||
if (val<0xf0) mpu->state.data_onoff++;
|
||||
else {
|
||||
mpu->state.data_onoff=-1;
|
||||
MPU401_EOIHandlerDispatch(mpu);
|
||||
return;
|
||||
}
|
||||
if (val==0) mpu->state.send_now=1;
|
||||
else mpu->state.send_now=0;
|
||||
mpu->condbuf.counter=val;
|
||||
break;
|
||||
case 1: /* Command byte #1 */
|
||||
mpu->condbuf.type=T_COMMAND;
|
||||
if (val==0xf8 || val==0xf9) mpu->condbuf.type=T_OVERFLOW;
|
||||
mpu->condbuf.value[mpu->condbuf.vlength]=val;
|
||||
mpu->condbuf.vlength++;
|
||||
if ((val&0xf0)!=0xe0) MPU401_EOIHandlerDispatch(mpu);
|
||||
else mpu->state.data_onoff++;
|
||||
break;
|
||||
case 2:/* Command byte #2 */
|
||||
mpu->condbuf.value[mpu->condbuf.vlength]=val;
|
||||
mpu->condbuf.vlength++;
|
||||
MPU401_EOIHandlerDispatch(mpu);
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
switch (mpu->state.data_onoff)
|
||||
{ /* Data */
|
||||
case -1:
|
||||
return;
|
||||
case 0: /* Timing byte */
|
||||
if (val<0xf0) mpu->state.data_onoff=1;
|
||||
else {
|
||||
mpu->state.data_onoff=-1;
|
||||
MPU401_EOIHandlerDispatch(mpu);
|
||||
return;
|
||||
}
|
||||
if (val==0) mpu->state.send_now=1;
|
||||
else mpu->state.send_now=0;
|
||||
mpu->playbuf[mpu->state.channel].counter=val;
|
||||
break;
|
||||
case 1: /* MIDI */
|
||||
mpu->playbuf[mpu->state.channel].vlength++;
|
||||
posd=mpu->playbuf[mpu->state.channel].vlength;
|
||||
if (posd==1) {
|
||||
switch (val&0xf0) {
|
||||
case 0xf0: /* System message or mark */
|
||||
if (val>0xf7) {
|
||||
mpu->playbuf[mpu->state.channel].type=T_MARK;
|
||||
mpu->playbuf[mpu->state.channel].sys_val=val;
|
||||
length=1;
|
||||
} else {
|
||||
//LOG(LOG_MISC,LOG_ERROR)("MPU-401:Illegal message");
|
||||
mpu->playbuf[mpu->state.channel].type=T_MIDI_SYS;
|
||||
mpu->playbuf[mpu->state.channel].sys_val=val;
|
||||
length=1;
|
||||
}
|
||||
break;
|
||||
case 0xc0: case 0xd0: /* MIDI Message */
|
||||
mpu->playbuf[mpu->state.channel].type=T_MIDI_NORM;
|
||||
length=mpu->playbuf[mpu->state.channel].length=2;
|
||||
break;
|
||||
case 0x80: case 0x90: case 0xa0: case 0xb0: case 0xe0:
|
||||
mpu->playbuf[mpu->state.channel].type=T_MIDI_NORM;
|
||||
length=mpu->playbuf[mpu->state.channel].length=3;
|
||||
break;
|
||||
default: /* MIDI data with running status */
|
||||
posd++;
|
||||
mpu->playbuf[mpu->state.channel].vlength++;
|
||||
mpu->playbuf[mpu->state.channel].type=T_MIDI_NORM;
|
||||
length=mpu->playbuf[mpu->state.channel].length;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!(posd==1 && val>=0xf0)) mpu->playbuf[mpu->state.channel].value[posd-1]=val;
|
||||
if (posd==length) MPU401_EOIHandlerDispatch(mpu);
|
||||
}
|
||||
}
|
||||
|
||||
static void MPU401_IntelligentOut(mpu_t *mpu, uint8_t chan)
|
||||
{
|
||||
uint8_t val;
|
||||
uint8_t i;
|
||||
switch (mpu->playbuf[chan].type)
|
||||
{
|
||||
case T_OVERFLOW:
|
||||
break;
|
||||
case T_MARK:
|
||||
val=mpu->playbuf[chan].sys_val;
|
||||
if (val==0xfc)
|
||||
{
|
||||
midi_write(val);
|
||||
mpu->state.amask&=~(1<<chan);
|
||||
mpu->state.req_mask&=~(1<<chan);
|
||||
}
|
||||
break;
|
||||
case T_MIDI_NORM:
|
||||
for (i=0;i<mpu->playbuf[chan].vlength;i++)
|
||||
midi_write(mpu->playbuf[chan].value[i]);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void UpdateTrack(mpu_t *mpu, uint8_t chan)
|
||||
{
|
||||
MPU401_IntelligentOut(mpu, chan);
|
||||
if (mpu->state.amask&(1<<chan))
|
||||
{
|
||||
mpu->playbuf[chan].vlength=0;
|
||||
mpu->playbuf[chan].type=T_OVERFLOW;
|
||||
mpu->playbuf[chan].counter=0xf0;
|
||||
mpu->state.req_mask|=(1<<chan);
|
||||
} else {
|
||||
if (mpu->state.amask==0 && !mpu->state.conductor) mpu->state.req_mask|=(1<<12);
|
||||
}
|
||||
}
|
||||
|
||||
static void UpdateConductor(mpu_t *mpu)
|
||||
{
|
||||
if (mpu->condbuf.value[0]==0xfc)
|
||||
{
|
||||
mpu->condbuf.value[0]=0;
|
||||
mpu->state.conductor=0;
|
||||
mpu->state.req_mask&=~(1<<9);
|
||||
if (mpu->state.amask==0) mpu->state.req_mask|=(1<<12);
|
||||
return;
|
||||
}
|
||||
mpu->condbuf.vlength=0;
|
||||
mpu->condbuf.counter=0xf0;
|
||||
mpu->state.req_mask|=(1<<9);
|
||||
}
|
||||
|
||||
//Updates counters and requests new data on "End of Input"
|
||||
static void MPU401_EOIHandler(void *p, uint8_t val)
|
||||
{
|
||||
mpu_t *mpu = (mpu_t *)p;
|
||||
|
||||
mpu->state.eoi_scheduled=0;
|
||||
if (mpu->state.send_now)
|
||||
{
|
||||
mpu->state.send_now=0;
|
||||
if (mpu->state.cond_req) UpdateConductor(mpu);
|
||||
else UpdateTrack(mpu, mpu->state.channel);
|
||||
}
|
||||
mpu->state.irq_pending=0;
|
||||
if (!mpu->state.playing || !mpu->state.req_mask) return;
|
||||
uint8_t i=0;
|
||||
do {
|
||||
if (mpu->state.req_mask&(1<<i)) {
|
||||
QueueByte(mpu, 0xf0+i);
|
||||
mpu->state.req_mask&=~(1<<i);
|
||||
break;
|
||||
}
|
||||
} while ((i++)<16);
|
||||
}
|
||||
|
||||
static void MPU401_EOIHandlerDispatch(void *p)
|
||||
{
|
||||
mpu_t *mpu = (mpu_t *)p;
|
||||
|
||||
if (mpu->state.send_now)
|
||||
{
|
||||
mpu->state.eoi_scheduled=1;
|
||||
MPU401_EOIHandler(mpu, 0.06f); //Possible a bit longer
|
||||
}
|
||||
else if (!mpu->state.eoi_scheduled)
|
||||
MPU401_EOIHandler(mpu, 0);
|
||||
}
|
||||
|
||||
static void mpu401_write(uint16_t addr, uint8_t val, void *p)
|
||||
{
|
||||
mpu_t *mpu = (mpu_t *)p;
|
||||
|
||||
pclog("MPU401 Write Port %04X, val %x\n", addr, val);
|
||||
|
||||
switch (addr & 1)
|
||||
{
|
||||
case 0: /*Data*/
|
||||
MPU401_WriteData(mpu, val);
|
||||
break;
|
||||
|
||||
case 1: /*Command*/
|
||||
MPU401_WriteCommand(mpu, val);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t mpu401_read(uint16_t addr, void *p)
|
||||
{
|
||||
mpu_t *mpu = (mpu_t *)p;
|
||||
uint8_t ret;
|
||||
|
||||
switch (addr & 1)
|
||||
{
|
||||
case 0: //Read Data
|
||||
ret = MSG_MPU_ACK;
|
||||
if (mpu->queue_used)
|
||||
{
|
||||
if (mpu->queue_pos>=MPU401_QUEUE) mpu->queue_pos-=MPU401_QUEUE;
|
||||
ret=mpu->queue[mpu->queue_pos];
|
||||
mpu->queue_pos++;mpu->queue_used--;
|
||||
}
|
||||
if (!mpu->intelligent) return ret;
|
||||
|
||||
if (mpu->queue_used == 0) picintc(1 << mpu->irq);
|
||||
|
||||
if (ret>=0xf0 && ret<=0xf7)
|
||||
{ /* MIDI data request */
|
||||
mpu->state.channel=ret&7;
|
||||
mpu->state.data_onoff=0;
|
||||
mpu->state.cond_req=0;
|
||||
}
|
||||
if (ret==MSG_MPU_COMMAND_REQ)
|
||||
{
|
||||
mpu->state.data_onoff=0;
|
||||
mpu->state.cond_req=1;
|
||||
if (mpu->condbuf.type!=T_OVERFLOW)
|
||||
{
|
||||
mpu->state.block_ack=1;
|
||||
MPU401_WriteCommand(mpu, mpu->condbuf.value[0]);
|
||||
if (mpu->state.command_byte) MPU401_WriteData(mpu, mpu->condbuf.value[1]);
|
||||
}
|
||||
mpu->condbuf.type=T_OVERFLOW;
|
||||
}
|
||||
if (ret==MSG_MPU_END || ret==MSG_MPU_CLOCK || ret==MSG_MPU_ACK) {
|
||||
mpu->state.data_onoff=-1;
|
||||
MPU401_EOIHandlerDispatch(mpu);
|
||||
}
|
||||
break;
|
||||
|
||||
case 1: //Read Status
|
||||
mpu->status = 0x3f; /* Bits 6 and 7 clear */
|
||||
if (mpu->state.cmd_pending) ret|=STATUS_OUTPUT_NOT_READY;
|
||||
if (!mpu->queue_used) ret|=STATUS_INPUT_NOT_READY;
|
||||
return mpu->status;
|
||||
}
|
||||
pclog("MPU401 Read Port %04X, ret %x\n", addr, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static void MPU401_Event(void *p)
|
||||
{
|
||||
mpu_t *mpu = (mpu_t *)p;
|
||||
uint8_t i;
|
||||
|
||||
if (mpu->mode==M_UART) return;
|
||||
if (mpu->state.irq_pending) goto next_event;
|
||||
for (i=0;i<8;i++) { /* Decrease counters */
|
||||
if (mpu->state.amask&(1<<i)) {
|
||||
mpu->playbuf[i].counter--;
|
||||
if (mpu->playbuf[i].counter<=0) UpdateTrack(mpu, i);
|
||||
}
|
||||
}
|
||||
if (mpu->state.conductor) {
|
||||
mpu->condbuf.counter--;
|
||||
if (mpu->condbuf.counter<=0) UpdateConductor(mpu);
|
||||
}
|
||||
if (mpu->clock.clock_to_host) {
|
||||
mpu->clock.cth_counter++;
|
||||
if (mpu->clock.cth_counter >= mpu->clock.cth_rate) {
|
||||
mpu->clock.cth_counter=0;
|
||||
mpu->state.req_mask|=(1<<13);
|
||||
}
|
||||
}
|
||||
if (!mpu->state.irq_pending && mpu->state.req_mask) MPU401_EOIHandler(mpu, 0);
|
||||
next_event:
|
||||
mpu401_event_callback = 0;
|
||||
int new_time;
|
||||
if ((new_time=mpu->clock.tempo*mpu->clock.timebase)==0) return;
|
||||
mpu401_event_callback = MPU401_TIMECONSTANT/new_time;
|
||||
}
|
||||
|
||||
void mpu401_init(mpu_t *mpu, uint16_t addr, int irq, int mode)
|
||||
{
|
||||
mpu->status = STATUS_INPUT_NOT_READY;
|
||||
mpu->irq = irq;
|
||||
mpu->queue_used = 0;
|
||||
mpu->queue_pos = 0;
|
||||
mpu->mode = mode;
|
||||
|
||||
io_sethandler(addr, 0x0002, mpu401_read, NULL, NULL, mpu401_write, NULL, NULL, mpu);
|
||||
timer_add(MPU401_Event, &mpu401_event_callback, &mpu401_event_callback, mpu);
|
||||
|
||||
MPU401_Reset(mpu);
|
||||
}
|
61
src/SOUND/snd_mpu401.h
Normal file
61
src/SOUND/snd_mpu401.h
Normal file
@@ -0,0 +1,61 @@
|
||||
#define MPU401_VERSION 0x15
|
||||
#define MPU401_REVISION 0x01
|
||||
#define MPU401_QUEUE 32
|
||||
#define MPU401_TIMECONSTANT (60000000/1000.0f)
|
||||
#define MPU401_RESETBUSY 27.0f
|
||||
|
||||
typedef enum MpuMode { M_UART,M_INTELLIGENT } MpuMode;
|
||||
typedef enum MpuDataType {T_OVERFLOW,T_MARK,T_MIDI_SYS,T_MIDI_NORM,T_COMMAND} MpuDataType;
|
||||
|
||||
/* Messages sent to MPU-401 from host */
|
||||
#define MSG_EOX 0xf7
|
||||
#define MSG_OVERFLOW 0xf8
|
||||
#define MSG_MARK 0xfc
|
||||
|
||||
/* Messages sent to host from MPU-401 */
|
||||
#define MSG_MPU_OVERFLOW 0xf8
|
||||
#define MSG_MPU_COMMAND_REQ 0xf9
|
||||
#define MSG_MPU_END 0xfc
|
||||
#define MSG_MPU_CLOCK 0xfd
|
||||
#define MSG_MPU_ACK 0xfe
|
||||
|
||||
typedef struct mpu_t
|
||||
{
|
||||
int intelligent;
|
||||
MpuMode mode;
|
||||
int irq;
|
||||
uint8_t status;
|
||||
uint8_t queue[MPU401_QUEUE];
|
||||
int queue_pos,queue_used;
|
||||
struct track
|
||||
{
|
||||
int counter;
|
||||
uint8_t value[8],sys_val;
|
||||
uint8_t vlength,length;
|
||||
MpuDataType type;
|
||||
} playbuf[8],condbuf;
|
||||
struct {
|
||||
int conductor,cond_req,cond_set, block_ack;
|
||||
int playing,reset;
|
||||
int wsd,wsm,wsd_start;
|
||||
int run_irq,irq_pending;
|
||||
int send_now;
|
||||
int eoi_scheduled;
|
||||
int data_onoff;
|
||||
uint32_t command_byte,cmd_pending;
|
||||
uint8_t tmask,cmask,amask;
|
||||
uint16_t midi_mask;
|
||||
uint16_t req_mask;
|
||||
uint8_t channel,old_chan;
|
||||
} state;
|
||||
struct {
|
||||
uint8_t timebase,old_timebase;
|
||||
uint8_t tempo,old_tempo;
|
||||
uint8_t tempo_rel,old_tempo_rel;
|
||||
uint8_t tempo_grad;
|
||||
uint8_t cth_rate,cth_counter;
|
||||
int clock_to_host,cth_active;
|
||||
} clock;
|
||||
} mpu_t;
|
||||
|
||||
void mpu401_init(mpu_t *mpu, uint16_t addr, int irq, int mode);
|
Reference in New Issue
Block a user