home *** CD-ROM | disk | FTP | other *** search
- #include <stdlib.h>
- #include <dos.h>
- #include "std.h"
- #include "mpuregs.h"
- #include "mpuregs.h"
-
- #define MPU_PRIVATES
- #include "mpu.h"
-
- // Manifest constants
-
- #if 0
- #define DEFAULT_BASE_ADDRESS 0x330 // Default MPU base address
- #define DEFAULT_INTERRUPT 2 // Default MPU interrrupt
- #define DEFAULT_READ_BUFFER_SIZE 1024 // Default read buffer size
- #define SEND_RETRIES 0xFFF // Number of times to try a send
- #define EXTRA_READS_ON_OPEN 0x300 // Number of attempts to clear RECEIVE READY when opening
- #define MAX_TIMING_BYTE_DELAY 0xFFF
- #define RECEIVE_TRIES 3 // Number of attempts on READY_TO_RECEIVE
- #define SEND_TRIES 3 // Number of attempts on READY_TO_SEND
- #else
- /*
- I had to increase these constants to get things to work
- on my 16mhz 286. I don't know yet if I have some kind of system
- specific problem, nor have I narrowed down exactly which
- constants needed to be increased. - lt
- */
- #define DEFAULT_BASE_ADDRESS 0x330 // Default MPU base address
- #define DEFAULT_INTERRUPT 2 // Default MPU interrrupt
- #define DEFAULT_READ_BUFFER_SIZE 1024 // Default read buffer size
- #define SEND_RETRIES 0x3FFF // Number of times to try a send
- #define EXTRA_READS_ON_OPEN 0xf00 // Number of attempts to clear RECEIVE READY when opening
- #define MAX_TIMING_BYTE_DELAY 0x3FFF
- #define RECEIVE_TRIES 12 // Number of attempts on READY_TO_RECEIVE
- #define SEND_TRIES 12 // Number of attempts on READY_TO_SEND
- #endif
-
- #define TRACE_IN
- #define TRACE_OUT
-
- #if defined(TRACE_IN) || defined(TRACE_OUT)
-
- #define LOGSIZE 4096
-
- struct LogEntry {
- unsigned char tag;
- unsigned short byte;
- };
- struct MpuLog {
- struct LogEntry d[LOGSIZE];
- int tail;
- };
- struct MpuLog mpulog;
-
- #endif
-
-
- #ifdef DOC
-
- DESCRIPTION
-
- Returns a string error message corresponding to the supplied mpu error
- number.
- #endif
-
- char *MpuErrorString(int mpuErrno) {
- return mpuErrorStrings[mpuErrno];
- }
-
- #ifdef DOC
- DESCRIPTION
-
- Returns most recent error code, or 0 if successful.
- #endif
-
- int GetMpuStatus(MpuPortT *mpu) {
- return mpu->mpuErrno;
- }
-
- #ifdef PDOC
-
- DESCRIPTION
-
- Is MPU data ready to send? This routine delays some to make sure.
-
- RETURNS
-
- NO = No data
- YES = Data ready.
- #endif
-
- PRIVATE BOOL isReadyToSend(MpuPortT *mpu) {
- int retries;
- int TheStatus;
- volatile unsigned int j = 0;
- do
- {
- TheStatus = inportb(mpu->statusPort);
- j++;
- }
- while ( ((TheStatus & MPU_NOT_READY_TO_SEND) != 0) && (j < SEND_TRIES));
- return (j != SEND_TRIES);
- }
- #ifdef PDOC
-
- DESCRIPTION
-
- Is MPU data ready to receive? This routine delays some to make sure.
-
- RETURNS
-
- NO = No data
- YES = Data ready.
- #endif
-
- PRIVATE BOOL isReadyToReceive(MpuPortT *mpu) {
- int retries;
- int TheStatus;
- volatile unsigned int j = 0;
- do
- {
- TheStatus = inportb(mpu->statusPort);
- j++;
- }
- while ( ((TheStatus & MPU_NOT_READY_TO_RECEIVE) != 0) && (j < RECEIVE_TRIES));
- return (j != RECEIVE_TRIES);
- }
-
- #ifdef PDOC
-
- DESCRIPTION
-
- Check to see if data is waiting.
-
- RETURNS
- NO -> No data waiting
- YES -> Data waiting
- #endif
-
- BOOL DataReady(MpuPortT *mpu) {
- return (mpu->dataHead != mpu->dataTail);
- }
-
-
- int SendData(MpuPortT *mpu,UCHAR data) {
-
- int TheStatus;
- // Wait for ready
- #ifdef TRACE_OUT
- mpulog.d[mpulog.tail].tag = 'O';
- mpulog.d[mpulog.tail].byte = data;
- if( ++mpulog.tail >= LOGSIZE ) mpulog.tail = 0;
- #endif
- do {
- TheStatus = inportb(mpu->statusPort);
- } while ( ((TheStatus & MPU_NOT_READY_TO_SEND) != 0));
- #ifdef JUNK
- if (!isReadyToSend(mpu)) {
- mpu->mpuErrno = MPU_EHARDWARE;
- return NO;
- }
- #endif
-
- outp(mpu->dataPort,data);
- return YES;
- }
-
-
-
- #ifdef PDOC
-
- DESCRIPTION
-
- Interrupt handlers for MPU-401. Up to 4 MPU's supported.
- Four separate entry points allow an MpuPortT control block
- to be associated with the interrupt.
-
- #endif
-
- PRIVATE MpuPortT *mpuForInterrupt[4];
-
-
- PRIVATE void handleInterrupt(int portNum)
- {
- MpuPortT *mpu = mpuForInterrupt[portNum];
- int tail;
- int data;
- int track;
-
- enable();
- if (mpu != NULL) { /* Guard against spurious interrupts */
- while (( inp(mpu->statusPort) & MPU_NOT_READY_TO_RECEIVE) == 0) {
- tail = mpu->dataTail;
- mpu->readBuffer[tail] = data = inp(mpu->dataPort);
- #ifdef TRACE_IN
- mpulog.d[mpulog.tail].tag = 'I';
- mpulog.d[mpulog.tail].byte = data;
- if( ++mpulog.tail >= LOGSIZE ) mpulog.tail = 0;
- #endif
-
- if (data >= 0xF0) {
- if (data == 0xFF)
- mpu->_inSysex = YES;
- else if (data == 0xF7)
- mpu->_inSysex = NO;
- else if (data == 0xF9 ) {
- SendData(mpu,0xF8);
- }
- else if (data < 0xF7 && !mpu->_inSysex) {
- // callback track request
- track = data-0xF0;
- if (mpu->trackRequestHandler != NULL) {
- (*mpu->trackRequestHandler)(mpu->requestHandlerData,track);
- } else {
- SendData(mpu,0xF8);
- }
- }
- }
-
- ++tail;
- if (tail >= mpu->bufferSize)
- tail = 0;
- if (tail == mpu->dataHead) {
- mpu->overrunError = YES;
- } else {
- mpu->dataTail = tail;
- }
- }
- }
- disable();
- outportb(0x20, 0x20);
- }
-
- PRIVATE void interrupt mpuInterrupt0(void) {
- handleInterrupt(0);
- }
- PRIVATE void interrupt mpuInterrupt1(void) {
- handleInterrupt(1);
- }
- PRIVATE void interrupt mpuInterrupt2(void) {
- handleInterrupt(2);
- }
-
- PRIVATE void interrupt mpuInterrupt3(void) {
- handleInterrupt(3);
- }
-
- typedef void interrupt interruptHandlerT(void);
-
- PRIVATE interruptHandlerT far * mpuInterrupt[4] = {
- mpuInterrupt0,
- mpuInterrupt1,
- mpuInterrupt2,
- mpuInterrupt3
- };
-
- #ifdef PDOC
-
- DESCRIPTION
-
- Install an interrupt handler. Four separate interrupt handlers are
- available. The first unused interrupt handler is assigned to the
- supplied mpu port.
-
- RETURNS
-
- YES-> success
- NO -> failure.
- Call GetMpuStatus for error number.
-
- #endif
-
- int installInterruptHandler(MpuPortT *mpu) {
- int i;
- for (i = 0; i < 4 && mpuForInterrupt[i] != NULL; ++i)
- NOTHING;
- if (i == 4) {
- mpu->mpuErrno = MPU_NOINTERRUPTHANDLERS;
- return NO;
- }
- mpuForInterrupt[i] = mpu;
-
- mpu->oldInterrupt = getvect(mpu->mpuInterrupt+8);
- setvect(mpu->mpuInterrupt+8,mpuInterrupt[i]);
-
- /* Enable IRQ */
- outportb(0x21,inportb(0x21) & ((~0x80) >> mpu->mpuInterrupt));
- return YES;
- }
-
- #ifdef PDOC
-
- DESCRIPTION
-
- Removes the interrupt handler associated with the mpu port.
- #endif
-
- PRIVATE void removeInterrupt(MpuPortT *mpu) {
- int i;
-
- // Mask interrupt
- outportb(0x21,inportb(0x21) | ((0x80) >> mpu->mpuInterrupt));
- outportb(0x20,0x20); // Just in case interrupt fired while we were disabling it!!!
-
- // Remove interrupt handler
- setvect(mpu->mpuInterrupt+8,mpu->oldInterrupt);
-
- for (i = 0; i < 4 & mpuForInterrupt[i] != mpu; ++i)
- NOTHING;
- if (i != 4) {
- mpuForInterrupt[i] = NULL;
- }
- }
- #ifdef DOC
-
- DESCRIPTION
- Send one command byte
-
- RETURNS
- YES = success.
- NO = failure.
- mpuErrorCode set on failure.
- #endif
-
-
- int SendCommand(MpuPortT *mpu,UCHAR cmd) {
- #ifdef TRACE_CMDS
- printf("Cmd(%02X) ",cmd);
- #endif
- FOREVER { // Until byte sent
-
- // Wait for ready
- if (!isReadyToSend(mpu)) {
- mpu->mpuErrno = MPU_EHARDWARE;
- return NO;
- }
-
- disable();
- outp(mpu->commandPort,cmd);
- while ((inp(mpu->statusPort) & MPU_NOT_READY_TO_RECEIVE) != 0)
- NOTHING;
- if (inp(mpu->dataPort) == ACK_MSG) {
- enable();
- return YES;
- }
- geninterrupt(mpu->mpuInterrupt); // Regenerate interrupt for some reason
- enable();
- }
- }
-
-
- #ifdef PDOC
-
- DESCRIPTION
-
- Send one command byte. For use during initialization ONLY.
- This routine reads and discards any extraneous data bytes that
- may occur during an attempt to write a command byte.
-
- RETURNS
- YES = success.
- NO = failure.
- mpuErrorCode set on failure.
- #endif
-
- int sendInitCommand(MpuPortT *mpu,UCHAR cmd) {
- int j;
-
- #ifdef TRACE_CMDS
- printf("Cmd(%02X) ",cmd);
- #endif
-
- FOREVER { // Until byte sent
-
- // Wait for ready
- while (!isReadyToSend(mpu)) {
- if (((inp(mpu->statusPort)) & MPU_NOT_READY_TO_RECEIVE) == 0) {
- j = 0;
- while ((( inp(mpu->statusPort)) & MPU_NOT_READY_TO_RECEIVE) == 0) {
- inp(mpu->dataPort); /* Discard a received message */
- if (j++ > EXTRA_READS_ON_OPEN) {
- mpu->mpuErrno = MPU_EHARDWARE;
- return NO;
- }
- }
- } else {
- mpu->mpuErrno = MPU_EHARDWARE;
- return NO;
- }
- }
-
- disable();
- outp(mpu->commandPort,cmd);
- while (((inp(mpu->statusPort)) & MPU_NOT_READY_TO_RECEIVE) != 0)
- NOTHING;
- if (inp(mpu->dataPort) == ACK_MSG) {
- enable();
- return YES;
- }
- enable();
- }
- }
-
- #ifdef DOC
-
- DESCRIPTION
-
- Read a data byte from an MPU port.
-
- RETURNS
- -1 => error condition
- else a data byte
- #endif
-
- static int _traceReads = NO;
-
- int ReadData(MpuPortT *mpu) {
- int val;
-
- if (mpu->overrunError) {
- mpu->mpuErrno = MPU_OVERRUN;
- return -1;
- }
- if (mpu->ungetChar != -1) {
- val = mpu->ungetChar;
- mpu->ungetChar = -1;
- return val;
- }
- while (!DataReady(mpu))
- NOTHING;
- val = mpu->readBuffer[mpu->dataHead];
- ++mpu->dataHead;
- if (mpu->dataHead == mpu->bufferSize) {
- mpu->dataHead = 0;
- }
- if (_traceReads) {
- printf("(%02X)",val);
- }
- return val;
- }
-
- #ifdef DOC
-
- DESCRIPTION
-
- Return the last character to the read buffer. Only one character may be
- ungotten.
- #endif
-
- void UngetData(MpuPortT *mpu,UCHAR data) {
- mpu->ungetChar = data;
- }
- #ifdef DOC
-
- DESCRIPTION
-
- Create an MPU port. If any of the parameters are zero, default values
- are used.
-
- RETURNS
-
- If NULL, then error. Call GetMpuError() to get error code.
- else, a pointer to a newly opened MpuPortT.
- #endif
-
- MpuPortT *CreateMpuPort(
- int base_address, // Mpu Base address (default 0x330)
- int interrupt_number, // Mpu Interrupt (default 2)
- int buffer_size, // Receive buffer size (default 1024)
- enum MpuOperatingModeT operating_mode, // Operating mode:
- // STOP_MODE,
- // RECORD_MODE,
- // PLAY_MODE,
- // RECORDPLAY_MODE
- enum MpuClockT clock_source, // MPU_INTERNAL_CLOCK
- // or MPU_MIDI_CLOCK
- // or MPU_FSK_CLOCK
- int tempo, // Beats per minute
- enum MpuTimebaseT timebase, // Ticks per beat (see MpuTimebaseT in MPU.H)
- int metronome_measure_length, // beats per measure,0-> metronome off
- int mode, // See DESCRIPTION
- int midi_channel_mask, // bit n controls midi channel n+1
- // bit = 0 -> pass through without host intervention
- // bit = 1 -> record/filter this midi channel
- int *result // retcode is placed here
- ) {
- static int timebaseTicks[] = {
- 48,72,96,120,144,168,192
- };
-
-
- MpuPortT *mpu;
- int t;
-
- if (result)
- *result = 0;
-
- if (base_address == 0)
- base_address = DEFAULT_BASE_ADDRESS;
- if (interrupt_number == 0)
- interrupt_number = DEFAULT_INTERRUPT;
- if (buffer_size == 0)
- buffer_size = DEFAULT_READ_BUFFER_SIZE;
-
- mpu = malloc(sizeof(MpuPortT));
- if (mpu == NULL) {
- if (result)
- *result = MPU_NOMEM;
- return NULL;
- }
-
- mpu->dataPort = base_address;
- mpu->commandPort = base_address+1;
- mpu->statusPort = base_address+1;
- mpu->mpuInterrupt = interrupt_number;
- mpu->currentTime = 0;
- mpu->ungetChar = -1;
- mpu->_inSysex = 0;
-
- mpu->readBuffer = malloc(buffer_size);
- if (mpu->readBuffer == NULL) {
- free(mpu);
- mpu->mpuErrno = MPU_NOMEM;
- return NULL;
- }
- mpu->bufferSize = buffer_size;
- mpu->dataHead = 0;
- mpu->dataTail = 0;
- mpu->overrunError = NO;
- mpu->currentMode = STOP_MODE;
- mpu->lastCommand = 0;
- mpu->trackRequestHandler = NULL;
- mpu->requestHandlerData = NULL;
- mpu->mpuErrno = 0;
-
- // Synch up on a timing byte to start.
- for (t = 0; t < MAX_TIMING_BYTE_DELAY; ++t) {
- if ((( inp(mpu->statusPort)) & MPU_NOT_READY_TO_RECEIVE) == 0) {
- inp(mpu->dataPort);
- }
- }
-
- if (!sendInitCommand(mpu,MPU_RESET_CMD)) {
- *result = MPU_NOHARDWARE;
- free(mpu->readBuffer);
- free(mpu);
- return NULL;
- }
-
- switch (clock_source) {
- case MPU_INTERNAL_CLOCK:
- sendInitCommand(mpu,INT_CLOCK_CMD);
- break;
- case MPU_MIDI_CLOCK:
- sendInitCommand(mpu,MIDI_CLOCK_CMD);
- break;
- case MPU_FSK_CLOCK:
- sendInitCommand(mpu,FSK_CLOCK_CMD);
- break;
- }
- sendInitCommand(mpu,TIMING_BYTE_ALWAYS_CMD);
- if (mode & MPU_RX_MODE) {
- sendInitCommand(mpu,MODE_MESS_ON_CMD);
- }
- if (mode & MPU_EXCLUSIVE_THRU) {
- sendInitCommand(mpu,EXCLUSIVE_THRU_ON_CMD);
- }
- if (mode & MPU_RX_COMMON) {
- sendInitCommand(mpu,COMMON_TO_HOST_ON_CMD);
- }
- if (MPU_RX_REALTIME) {
- sendInitCommand(mpu,REAL_TIME_TO_HOST_ON_CMD);
- }
- if (metronome_measure_length) {
- sendInitCommand(mpu,MIDI_METRONOME_CMD);
- SendData(mpu,metronome_measure_length);
- sendInitCommand(mpu,METRONOME_W_ACCENTS_CMD);
- } else {
- sendInitCommand(mpu,METRONOME_OFF_CMD);
- }
- if (mode & MPU_RX_BENDER) {
- sendInitCommand(mpu,BENDER_ON_CMD);
- } else {
- sendInitCommand(mpu,BENDER_OFF_CMD);
- }
- if (mode & MPU_VOICES_THRU)
- sendInitCommand(mpu,MIDI_THRU_ON_CMD);
- else
- sendInitCommand(mpu,MIDI_THRU_OFF_CMD);
- if (mode & MPU_RX_EXCLUSIVE)
- sendInitCommand(mpu,EXCLUSIVE_TO_HOST_ON_CMD);
- else
- sendInitCommand(mpu,EXCLUSIVE_TO_HOST_OFF_CMD);
- sendInitCommand(mpu,TIMEBASE_48_CMD+timebase);
- sendInitCommand(mpu,SET_TEMPO_CMD);
- SendData(mpu,tempo);
-
- sendInitCommand(mpu,MIDI_CHANNEL_MASK_LO_CMD);
- SendData(mpu,midi_channel_mask);
- sendInitCommand(mpu,MIDI_CHANNEL_MASK_HI_CMD);
- SendData(mpu,midi_channel_mask >> 8);
-
-
- if (!installInterruptHandler(mpu)) { // Can use normal SendCommand after this!
- free(mpu->readBuffer);
- free(mpu);
- return NULL;
- }
-
- sendInitCommand(mpu,CLEAR_PLAY_COUNTERS_CMD);
- sendInitCommand(mpu,CLEAR_RECORD_COUNTER_CMD);
-
- mpu->currentMode = STOP_MODE;
-
- SetMpuOperatingMode(mpu,operating_mode);
-
- return mpu;
- }
-
- #ifdef DOC
-
- DESCRIPTION
-
- Stops the MPU. Sets PLAY, RECORD and MIDI modes to STOP.
- Waits for MPU to complete pending PLAY operations.
- #endif
-
- void StopMpu(MpuPortT *mpu) {
- int data;
-
- switch (mpu->currentMode) {
- case PLAY_MODE:
- SendCommand(mpu,PLAY_STOP | MIDI_STOP);
- break;
- case RECORD_MODE:
- SendCommand(mpu,RECORD_STOP | MIDI_STOP);
- break;
- case RECORDPLAY_MODE:
- SendCommand(mpu,RECORD_STOP | PLAY_STOP | MIDI_STOP);
- break;
- }
- if (mpu->currentMode == RECORD_MODE
- || mpu->currentMode == RECORDPLAY_MODE) {
- // Wait for End of recording
- do {
- data = ReadData(mpu);
- } while (data != ALL_END_MSG && data != -1);
- }
- mpu->currentMode = STOP_MODE;
- }
-
-
- #ifdef DOC
- DESCRIPTION
-
- Set Mpu Operating mode:
-
- STOP_MODE: Don't send or receive messages.
- RECORD_MODE: Receive messages
- PLAY_MODE: Allow sending of scheduled messages, receive no messages.
- RECORDPLAY_MODE: Allow sending of scheduled messages, receive messages.
-
- BUGS
-
- Doesn't currently support CONTINUE operation.
-
- #endif
-
- void SetMpuOperatingMode(MpuPortT *mpu,MpuOperatingModeT operating_mode) {
- switch (operating_mode) {
- case STOP_MODE:
- StopMpu(mpu);
- break;
- case RECORD_MODE:
- SendCommand(mpu,RECORD_START | MIDI_START);
- break;
- case PLAY_MODE:
- SendCommand(mpu,PLAY_START | MIDI_START);
- break;
- case RECORDPLAY_MODE:
- SendCommand(mpu,PLAY_START | RECORD_START | MIDI_START);
- break;
- }
- }
-
- #ifdef DOC
-
- DESCRIPTION
-
- Destroy an MPU port. Deallocates memory, and removes the interrupt.
-
- RETURNS
-
- YES -> success.
- NO -> failure
- Call GetMpuStatus for error code.
- #endif
-
- BOOL DestroyMpu(MpuPortT *mpu) {
- if (mpu->currentMode != STOP_MODE) {
- StopMpu(mpu);
- }
- if (mpu == NULL)
- return YES;
- removeInterrupt(mpu);
- free(mpu->readBuffer);
- free(mpu);
- return YES;
- }
-
-
-
-
-