#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 6 (of 6)."
# Contents:  mpu.c
# Wrapped by lee@uhccux on Wed Mar 29 09:58:12 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'mpu.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'mpu.c'\"
else
echo shar: Extracting \"'mpu.c'\" \(32980 characters\)
sed "s/^X//" >'mpu.c' <<'END_OF_FILE'
X/*****************************************************************************
X*	    Change Log
X*  Date	    | Change
X*-----------+-----------------------------------------------------------------
X* 31-Dec-85 | Created changelog
X* 31-Dec-85 | Add c:\ to include directives
X* 31-Dec-85 | Call setctl/clrctl to deal with ctrl-break intercept.  Note that
X*	    | clrctl can be called several times, as might be the case during
X*	    | a ctrl-break abort
X*  1-Jan-86 | <rgd/jmn> Added keyloud
X*	    | Added initialized flag
X* 21-Jan-86 | Abort l_restuntil if Ctrl-C or Ctrl-Break is typed
X* 21-Jan-86 | Took out mpu_unknown, replaced with mpu_error_check
X*  3-Feb-86 | Fixed MIDI_PROGRAM and MIDI_CHANNEL to subtract one to account
X*	    | for MIDI encodings, e.g. channel 1 is represented by 0
X*  8-Feb-86 | Added midi_exclusive to turn on/off flag in mpu-401.
X*	    | Added midi_buffer function.
X* 18-Feb-86 | Changed midi_exclusive to exclusive.
X*	    | Added midi_exclusive to send an exclusive message.
X* 19-Feb-86 | Changed MC_SEND_... to MC_SND_... to avoid name clash
X* 23-Feb-86 | Added midi_control, midi_bend, midi_touch routines
X* 24-Feb-86 | Added call to init_asm from musicinit.
X* 26-Mar-86 | Added midi_cont call to enable continuous control messages
X* 18-Apr-86 | Cleaned up documentation, renamed midi_control to midi_ctrl
X*  2-Jun-86 | Added tuning definition code.
X* 30-Jun-86 | Added switch to use timdif.asm instead of MPU-401 for timing
X* 18-Jul-86 | Added DEBUG switch in parallel with DEBUG in AINTR.ASM
X* 29-Jul-86 | Changed trace flag checking to first initialization only.
X*  5-Aug-86 | Minor changes for Lattice C Version 3.00
X*****************************************************************************/
X
X/*
X * Driver for MPU-401
X *
X * This module initializes things and then issues commands to the
X * MPU-401 in response to higher-level operations like note( , , ).
X * Unlike examples in the MPU-401 manual, these routines do not wait
X * for Acks by polling the MPU-401.  Instead, Acks and all other
X * input are handled by the interrupt routine (see aintr.asm), which
X * sets a flag whenever it sees an Ack.	 Therefore, these routines
X * just poll the Ack flag rather than directly reading the MPU-401.
X * This avoids the problem of the MPU-401 having data queued in front
X * of the Ack message.
X */
X
X/*
X * Interface Specifications for aintr.asm to mpu.c:
X *
X * aintr.asm is an interrupt handler that also parses midi data and
X * queues it for receipt by mpu.c.  In this way, interrupts are hidden
X * from C programmers and yet the mpu-401 never hangs trying to deliver
X * data because the interrupt routine is always ready to read data.
X * 
X * Data is parsed by aintr.asm in order to know how much data to read.
X * To avoid reparsing at the C level, the data is buffered in a
X * queue of messages rather than a simple byte stream.	Each message
X * is a 4-byte block.  The first byte is a status byte and the following
X * bytes (one or two) are data bytes.
X * 
X * Note that timing is not specified.  Later, we may add a parallel
X * buffer to store 4-byte timing blocks if it seems necessary.
X * 
X * System exclusive messages are handled by copying the midi exclusive
X * data into a separate buffer provided by the application program
X * through the midi_buffer call.
X * 
X */
X
X/* define DEBUG to enable some extra debugging */
X/* #define DEBUG 1 */
X
X
X/* define TIMDIF to use the timdif.asm module */
X/* #define TIMDIF 1 */
X
X#include "cext.h"
X#include "stdio.h"
X#include "atxt.h"
X#include "mpu.h"
X#include "midicode.h"
X#include "cmdline.h"
X#include "userio.h"
X#include "pitch.h"
X#include "cintr.h"
X
X#ifdef TIMDIF
X#include "timdif.h"
X#endif
X
X#define BREAKTEST	if (CBREAK) {	/* Ctrl-C or Ctrl-Break handler */ \
X		al_nt_off();\
X		printf("Exiting.\n");\
X		musicterm();\
X		exit(1);\
X	}
X#define num_voices 16
X
X/****************************************************************************
X*
X* MPU 401 commands
X*
X****************************************************************************/
X
X#define MC_RESET		0xff
X#define MC_SET_TEMPO		0xe0
X#define MC_TIMEBASE_192		0xc8
X#define MC_START_RECORDING	0x22
X#define MC_RECORD_COUNTER	0xab
X#define MC_ON_METRONOME		0x85
X#define MC_VERSION		0xac
X#define MC_REVISION		0xad
X#define MC_NO_MEASURE_END	0x8c
X#define MC_SND_MIDI		0xd0
X#define MC_SND_EXCLUSIVE	0xdf
X#define MC_EXCLUSIVE		0x96
X#define MC_OFF_BEND		0x86
X#define MC_ON_BEND		0x87
X#define MC_OFF_THRU		0x88
X#define MC_ON_THRU		0x89
X
X/****************************************************************************
X*
X* useful defines and macros
X*
X****************************************************************************/
X
X#define MD_BEATS_PER_MINUTE	125	/* to get 400 ticks per sec */
X#define TICKS_TO_HUNDREDTHS(t)	((t) >> 2)
X#define HUNDREDTHS_TO_TICKS(h)	((h) << 2)
X#define NONE			(-1)
X
X/****************************************************************************
X*
X* MPU-401 interface to IBM-XT
X*
X****************************************************************************/
X
X#define DATAPORT	0x330		/* Input data port of MPU 401 */
X#define STATPORT	0x331		/* status and request port */
X#define COMPORT		0x331		/* Send MPU 401 commands to here */
X#define DSR		(1<<7)		/* This bit of STATPORT, when low,
X					   means the 401 has Data to send */
X#define DRR		(1<<6)		/* This bit of STATPORT, when low,
X					   means the 401 will take commands */
X#define ACK		0xfe		/* Acknowledgement data code */
X#define IRQ		2		/* Interrupt request line of 401 */
X#define MAX_ACK_WAIT 1000
X
X/****************************************************************************
X*
X* exported flags
X*
X****************************************************************************/
X
Xboolean musictrace = false;	/* enables printed trace of commands */
Xboolean miditrace = false;	/* enables printed trace of MIDI output */
X
X#define n_t_sw 2
Xprivate char *t_switches[n_t_sw] = { "-t", "-trace" };
X
X#define n_m_sw 2
Xprivate char *m_switches[n_m_sw] = { "-m", "-miditrace" };
X
X/****************************************************************************
X*
X* exported variables
X*
X****************************************************************************/
X
Xint keyloud;	/* set to velocity of last getkey event */
X
X
X/****************************************************************************
X*
X* variables shared with aintr.asm
X*
X****************************************************************************/
X
Xint	intnest = 0;	/* do not touch...read-only except by aintr.asm */
Xint	rd_delay = 0;	/* do not touch...read-only except by aintr.asm */
X
Xint	Unknown = ACK;
Xint	interror = 0;	/* reports errors from interrupt handler */
Xint	timeerr = 0;	/* reports timeout errors */
X
X#ifdef DEBUG
Xint	intcnt = 0;	/* Count of interrupts taken */
Xint	loop_cnt, loop_max;	/* iteration counts */
X#endif
X
Xlong	Ticks;		/* Number of clock ticks since init */
Xint	Ack;		/* True if command has been acknowledged */
Xchar	MidiTime, MidiStat = 0x90, Midi1, Midi2, Midi3; /* midi and time */
Xchar	exclflag = 0;	/* used by aintr,  DO NOT TOUCH */
Xint	time_req = 0;	/* set to 1 when Ack will be followed by data */
Xint	mpu_result;	/* the data following the Ack */
X
X
X/****************************************************************************
X*
X* Variable set by BREAK module
X*
X****************************************************************************/
X
Xint CBREAK;
X
X
X/****************************************************************************
X*
X* variable imported from cintr.c
X*
X****************************************************************************/
X
Xextern int enabled;
X
X
X/****************************************************************************
X*
X* local module variables
X*
X****************************************************************************/
X
Xprivate int initialized = false;  /* set by musicinit, cleared by musicterm */
Xprivate boolean tune_flag = false; /* set by musicinit, never cleared */
Xprivate boolean metroflag = false; /* flag to turn on metronome */
Xprivate boolean mpuflag = true;    /* true iff mpu401 present */
Xprivate int len;		/* length of trace string */
Xprivate int last_cmd = 0;	/* last mpu_command, used by trace */
Xprivate int user_scale = false; /* true if user-defined scale */
Xprivate int bend[num_voices];	/* current pitch bend on channel */
Xprivate pitch_table pit_tab[128];	/* scale definition */
X
X/* "temporary" instrumentation: how long should we wait? */
Xprivate int max_ack_wait = 0;		/* maintained by mpu_wait */
X
X/****************************************************************************
X*
X* functions declared in this module
X*
X****************************************************************************/
X
Xprivate void	fixup();
Xprivate void	mpu_command();
Xprivate void	mpu_drr_wait();
Xprivate int	mpu_read();
Xprivate void	mpu_wait();
Xprivate void	mpu_write();
Xprivate void	trace_mpu_command();
Xprivate void	wfa();
X
X
X/****************************************************************************
X*
X* Buffer 
X*	shares data with aintr.asm (the producer)
X*
X****************************************************************************/
X
X#define BUFFERSIZE 1024
Xbyte	buff[BUFFERSIZE];	/* data buffer */
Xint	buffhead = 0;		/* buffer pointers */
Xint	bufftail = 0;
X
X
X/****************************************************************************
X*
X* System exclusive buffer variables (shared with aintr.asm)
X*
X****************************************************************************/
X
Xbyte *xbuff = 0;	/* address of the user-supplied buffer */
Xint xbuffmas;		/* mask for circular buffer address calculation */
Xint xbuffhea = 0;	/* buffer pointers */
Xint xbufftai = 0;
X
X/****************************************************************************
X*				 exclusive
X* Inputs:
X*	boolean onflag -- set to true to receive midi exclusive data
X* Effect: 
X*	Tells MPU401 to read exclusive messages into buffer
X****************************************************************************/
X
Xvoid exclusive(onflag)
X    boolean onflag; /* on or off? */
X{
X	if  (!initialized) fixup();
X	if (musictrace)
X	    printf("exclusive: %d\n", onflag);
X	mpu_command(MC_EXCLUSIVE | (onflag ? 1 : 0));
X}
X
X/****************************************************************************
X*				     fixup
X* Effect: 
X*	Print error message and call musicinit
X****************************************************************************/
X
Xprivate void fixup()
X{
X    printf("You forgot to call musicinit.  I'll do it for you.\n");
X    musicinit();
X}
X
X/****************************************************************************
X*				    getbuf
X* Inputs:
X*	boolean waitflag: true if routine should wait for data
X*	byte * p: Pointer to data destination
X* Result: boolean
X*	true if data was written to *p
X*	false if data not written to *p
X* Effect: 
X*	copies data from buffer to *p
X*	will wait for buffer to become nonempty if waitflag is true
X****************************************************************************/
X
Xboolean getbuf(waitflag, p)
X   boolean waitflag;	/* true if routine should wait for data */
X   byte *p;	/* pointer to data destination */
X{
X/*    register int head;*/
X
X    if (!initialized) fixup();
X    if (waitflag) while (buffhead == bufftail) /* wait */ ;
X    else if (buffhead == bufftail) return false;
X
X    *(long *)p = *(long *)(buff+buffhead);
X    buffhead += 4;
X    if (buffhead >= BUFFERSIZE) buffhead = 0;
X
X/* the previous three lines are an optimization of:
X *    head = buffhead;
X *    *p++ = buff[head++];
X *    *p++ = buff[head++];
X *    *p++ = buff[head++];
X *    head++;
X *
X *    if (head >= BUFFERSIZE) head = 0;
X *    buffhead = head;
X */
X    return true;
X}
X
X/****************************************************************************
X*				    getkey
X* Inputs:
X*	boolean waitflag: true if wait until key depression, false if
X*			  return immediately
X* Result: int
X*	key number of key which has been depressed
X*	It returns -1 if waitflag is false and no key has been pressed
X*	If waitflag is true this routine will block until a key is pressed
X* Effect: 
X*	reads a key
X****************************************************************************/
X
Xint getkey(waitflag)
X{
X    byte msg[4];
X    int k;
X
X    if (!initialized) fixup();
X    while (true) {	/* process data until you find a note */
X	/* look for data and exit if none found */
X	/* NOTE: waitflag will force waiting until data arrives */
X	if (!getbuf(waitflag, msg)) { /* nothing there */
X	    k = -1;
X	    break;
X	} else if ((msg[0] & MIDI_CODE_MASK) == MIDI_ON_NOTE) {
X	    if (msg[2] == 0) { /* velocity 0 -> note off */
X		keyloud = 0;
X		k = (msg[1]-12) + 128;
X	    } else {
X		keyloud = msg[2];
X		k = (msg[1]-12);
X	    }
X	    break;
X	} else if ((msg[0] & MIDI_CODE_MASK) == MIDI_OFF_NOTE) {
X	    keyloud = 0;
X	    k = (msg[1]-12) + 128;
X	    break;
X	}
X    }
X    if (musictrace) {
X	if (k != -1) printf("getkey got %d\n", k);
X    }
X    return k;
X}
X
X/****************************************************************************
X*				    gettime
X* Result: long
X*	current timestamp from MPU-401
X*	 Return the time in 100ths of seconds since the last call to
X*	 musicinit or timereset
X* Effect: 
X*	Reads the MPU-401 time
X****************************************************************************/
X
X/* just to make sure we're using the timdif module: */
Xlong gettime()
X{
X    BREAKTEST	/* abort if user typed Ctrl Break */
X
X    if	(!initialized) fixup();
X#ifndef TIMDIF
X    time_req = 1;	/* tell aintr.asm to read extra byte */
X    mpu_command(MC_RECORD_COUNTER);		/* Read counter */
X    return TICKS_TO_HUNDREDTHS(Ticks);
X#else
X    return ibm_time();
X#endif
X}
X
X/****************************************************************************
X*				    l_rest
X* Inputs:
X*	long time: Amount of time to rest
X* Effect: 
X*	Waits until the amount of time specified has lapsed
X****************************************************************************/
X
Xvoid l_rest(time)
X    long time;
X{
X    if (!initialized) fixup();
X    l_restuntil(time + gettime());	
X}
X
X/****************************************************************************
X*				  l_restuntil
X* Inputs:
X*	long time: Event time to rest until
X* Effect: 
X*	Waits until the specified time has been reached (absolute time)
X****************************************************************************/
X
Xvoid l_restuntil(time)
X    long time;
X{
X    while(time > gettime()) ;
X}
X
X/****************************************************************************
X*				metronome
X* Inputs:
X*	int onflag: true or false
X* Effect:
X*	enables (true) or disables (false) MPU-401 metronome function.
X*	must be called before musicinit
X****************************************************************************/
X
Xvoid metronome(onflag)
X    int onflag;
X{
X    metroflag = onflag;
X}
X
X/****************************************************************************
X*				   midi_bend
X* Inputs:
X*	int channel: midi channel on which to send data
X*	int value: pitch bend value
X* Effect: 
X*	Sends a midi pitch bend message
X****************************************************************************/
X
Xvoid midi_bend(channel, value)
X    int channel, value;
X{
X    if	(!initialized) fixup();
X    if (musictrace)
X	printf("midi_bend: ch %d, val %d\n", channel, value);
X    bend[MIDI_CHANNEL(channel)] = value;
X    mpu_command(MC_SND_MIDI);
X    mpu_write(MIDI_BEND | MIDI_CHANNEL(channel));
X    mpu_write(MIDI_DATA(value));
X    mpu_write(MIDI_DATA(value>>7));
X}
X
X/****************************************************************************
X*				midi_buffer
X* Inputs:
X*	byte * buffer: the buffer address
X*	int size: number of bytes in buffer
X* Returns:
X*	false if size is less than 16 or buffer is NULL, otherwise true
X* Effect:
X*	tells interrupt routine to store system exclusive messages in
X*	buffer.	 The largest power of 2 bytes less than size will be
X*	used.  xbuffhea and xbufftai will be initialized to zero,
X*	and xbufftai will be one greater than the index of the last
X*	system exclusive byte read from mpu401.
X****************************************************************************/
X
Xint midi_buffer(buffer, size)
X    byte *buffer;
X    int size;
X{
X    int mask;
X    mask = 16 - 1;
X    if (size < 16 || buffer == NULL) return false;
X    while (mask < size && mask > 0) mask = (mask << 1) + 1;
X    xbuff = NULL;	/* turn off buffering */
X    xbuffmas = mask >> 1;
X    xbuffhea = xbufftai = 0;
X    xbuff = buffer;	/* set buffer, turn on buffering */
X    return true;
X}
X
X/****************************************************************************
X*				midi_cont
X* Inputs:
X*	boolean onflag: true or false
X* Effect:
X*	enables (true) or disables (false) continuous control info from
X*	MPU-401 to host.
X****************************************************************************/
X
Xvoid midi_cont(onflag)
X    boolean onflag;
X{
X    if (onflag) mpu_command(MC_ON_BEND);
X    else mpu_command(MC_OFF_BEND);
X}
X
X/****************************************************************************
X*				   midi_ctrl
X* Inputs:
X*	int channel: midi channel on which to send data
X*	int control: control number
X*	int value: control value
X* Effect: 
X*	Sends a midi control change message
X****************************************************************************/
X
Xvoid midi_ctrl(channel, control, value)
X    int channel, control, value;
X{
X    if (!initialized) fixup();
X    if (musictrace)
X	printf("midi_ctrl: ch %d, ctrl %d, val %d\n", 
X		channel, control, value);
X    mpu_command(MC_SND_MIDI);
X    mpu_write(MIDI_CTRL | MIDI_CHANNEL(channel));
X    mpu_write(MIDI_DATA(control));
X    mpu_write(MIDI_DATA(value));
X}
X
X/****************************************************************************
X*				 midi_exclusive
X* Inputs:
X*	byte * msg: pointer to a midi exclusive message, terminated by 0xF7
X* Effect: 
X*	Sends a midi exclusive message
X****************************************************************************/
X
Xvoid midi_exclusive(msg)
X    byte *msg;	/* the data to be sent */
X{
X    int i;	/* can DX7 keep up? */
X
X    /* if user mistakenly called midi_exclusive instead of exclusive,
X     * the argument will be true or false, both of which are highly	
X     * unlikely valid arguments for midi_exclusive:
X     */
X    if (msg == (byte *) false || msg == (byte *) true) {
X	printf("midi_exclusive: invalid argument %d.\n", (int) msg);
X	if (initialized) musicterm();
X	exit(1);
X    }
X
X    if	(!initialized) fixup();
X    if (musictrace) printf("midi_exclusive\n");
X    mpu_command(MC_SND_EXCLUSIVE);
X    while (*msg != MIDI_EOX) {
X	mpu_write(*msg);
X	msg++;
X	/* This is a delay loop.  Without it, your DX7 will crash. */
X	for (i = (ATXT() == ISAT ? 4 : 2); i > 0; i--) {
X	    BREAKTEST
X	}
X    }
X    mpu_write(MIDI_EOX);
X}
X
X/****************************************************************************
X*				   midi_note
X* Inputs:
X*	int channel: midi channel on which to send data
X*	int pitch: midi pitch code
X*	int velocity: velocity with which to sound it (0=> release)
X* Effect: 
X*	Sends a midi note-play request out
X****************************************************************************/
X
Xvoid midi_note(channel, pitch, velocity)
X    int channel, pitch, velocity;
X{
X    if	(!initialized) fixup();
X    if (musictrace)
X	printf("midi_note: ch %d, key %d, vel %d\n",
X	    channel, pitch, velocity);
X    if (user_scale) {
X	/* check for correct pitch bend */
X	if ((pit_tab[pitch+12].pbend != bend[MIDI_CHANNEL(channel)]) &&
X	    (velocity != 0)) {
X	    midi_bend(channel, pit_tab[pitch+12].pbend);
X	    bend[channel] = pit_tab[pitch+12].pbend;
X	}
X	pitch = pit_tab[pitch+12].ppitch;
X    }
X    mpu_command(MC_SND_MIDI);
X    mpu_write(MIDI_ON_NOTE | MIDI_CHANNEL(channel));
X    mpu_write(MIDI_DATA(12 + pitch)); /* cmu standard to midi standard */
X    mpu_write(MIDI_DATA(velocity));
X}
X
X/****************************************************************************
X*				 midi_program
X* Inputs:
X*	int channel: Channel on which to send midi program change request
X*	int program: Program number to send (decremented by 1 before
X*			being sent as midi data)
X* Effect: 
X*	Sends a program change request out the channel
X****************************************************************************/
X
Xvoid midi_program(channel, program)
X    int channel;	/* midi channel */
X    int program;	/* the program number */
X{
X    if	(!initialized) fixup();
X    if (musictrace)
X	printf("midi_program: ch %d, prog %d\n",
X		channel, program);
X    mpu_command(MC_SND_MIDI);
X    mpu_write(MIDI_CH_PROGRAM | MIDI_CHANNEL(channel));
X    mpu_write(MIDI_PROGRAM(program));
X}
X
X/****************************************************************************
X*				midi_thru
X* Inputs:
X*	boolean onflag: true or false
X* Effect:
X*	enables (true) or disables (false) midi thru info from
X*	MPU-401 to host.  (Default is set; reset with cmdline -block.)
X****************************************************************************/
X
Xvoid midi_thru(onflag)
X    boolean onflag;
X{
X    if (onflag) mpu_command(MC_ON_THRU);
X    else mpu_command(MC_OFF_THRU);
X}
X
X/****************************************************************************
X*				   midi_touch
X* Inputs:
X*	int channel: midi channel on which to send data
X*	int value: control value
X* Effect: 
X*	Sends a midi after touch message
X****************************************************************************/
X
Xvoid midi_touch(channel, value)
X    int channel, value;
X{
X    if	(!initialized) fixup();
X    if (musictrace)
X	printf("midi_touch: ch %d, val %d\n", channel, value);
X    mpu_command(MC_SND_MIDI);
X    mpu_write(MIDI_TOUCH | MIDI_CHANNEL(channel));
X    mpu_write(MIDI_DATA(value));
X}
X
X/****************************************************************************
X*				   mpu_command
X* Inputs:
X*	int c: Character to write to MPU-401 command port
X* Effect: 
X*	Writes the data to the MPU-401 command port
X****************************************************************************/
X
Xprivate void mpu_command(c)
X    int c;
X{
X    if (!mpuflag) { /* simulated */
X	trace_mpu_command(c);
X    } else { /* real */
X	if (miditrace) trace_mpu_command(c);
X	mpu_drr_wait();
X	Ack = 0;
X	outp(COMPORT, c);
X	if (enabled) mpu_wait();
X	else wfa();
X    }
X}
X
X/****************************************************************************
X*				 mpu_drr_wait
X* Effect: 
X*	Waits until the MPU-401 is ready to receive data
X****************************************************************************/
X
X#define MAX_TRIES	2000
X
Xprivate void mpu_drr_wait()
X{
X    int i;
X
X    if (!mpuflag) return;	/* always ready if not there! */
X
X    for (i = 0; i < MAX_TRIES; i++)
X	if ((inp(STATPORT) & DRR) == 0) break;
X#ifdef DEBUG
X    if (i == MAX_TRIES)
X	printf("mpu-401 not ready to receive; intcnt=%d\n",intcnt);
X#endif
X}
X
X/****************************************************************************
X*				  mpu_error_check
X* Effect: 
X*	Reports any errors originating in the interrupt handler
X****************************************************************************/
X
Xvoid mpu_error_check()
X{
X    if (Unknown != ACK) {
X	printf("Unknown command: %x\n", Unknown);
X	Unknown = ACK;
X    }
X    if (interror != 0) {
X	char *cause;
X	switch (interror) {
X	    case NESTERR: cause = "nested interrupts";	break;
X	    case BUFFERR: cause = "buffer overflow";	break;
X	    case CMDERR:  cause = "unknown command";	break;
X	    default: cause = "";		   break;
X	}
X	printf("interror: %s\n", cause);
X	if (*cause == 0) printf("%d\n", interror);
X
X	interror = 0;
X    }
X    if (timeerr != 0) {
X	if (timeerr == TIMEOUT) printf("timeerr: timeout error\n");
X	else printf("timeerr = %d\n", timeerr);
X	timeerr = 0;
X    }
X}
X
X/****************************************************************************
X*				   mpu_read
X* Result: int
X*	character read from MPU-401
X* Effect: 
X*	Reads the MPU-401
X****************************************************************************/
X
Xprivate int mpu_read()
X{
X    int delay;
X    for (delay = 0; delay < 2000; delay++) {
X	if ((inp(STATPORT) & DSR) == 0)
X	    return inp(DATAPORT);
X	}
X#ifdef DEBUG
X    printf("mpu_read: DSR never went low, returning 0, intcnt=%d\n",intcnt);
X#endif
X    return 0;
X}
X
X/****************************************************************************
X*				   mpu_wait
X* Effect: 
X*	Called when interrupts are enabled.  Polls the 'Ack' flag, which is
X*	set by the interrupt handler.  If more than MAX_ACK_WAIT iterations
X*	occur without 'Ack' being set, issues an error message.
X*	Ack is cleared when it is detected.
X****************************************************************************/
X
Xprivate void mpu_wait()
X{
X    int ackcnt; /* delay counter */
X
X    if (!mpuflag) return;
X
X    for (ackcnt = 0; ackcnt < MAX_ACK_WAIT; ackcnt++) {
X	if (Ack) {
X	    if (max_ack_wait < ackcnt) max_ack_wait = ackcnt;
X	    Ack = 0;
X	    return;
X	}
X    }
X#ifdef DEBUG
X    printf("mpu_wait: No ack; incnt = %d\n",intcnt);
X#endif
X}
X
X/****************************************************************************
X*				   mpu_write
X* Inputs:
X*	int c: Character to write to MPU-401 data port
X* Effect: 
X*	Writes the data to the MPU-401 data port
X****************************************************************************/
X
Xprivate void mpu_write(c)
X    int c;
X{
X    if (!mpuflag) { /* simulate */
X	printf("%02x",c);
X	len += 2;
X    } else { /* real */
X	if (miditrace) { /* trace */
X	    printf("%02x",c);
X	    len += 2;
X	} /* trace */
X	mpu_drr_wait();
X	outp(DATAPORT, c);
X    }
X}
X
X/****************************************************************************
X*				mpuexists
X* Inputs:
X*	boolean flag: true or false
X* Effect:
X*	if argument is false, indicates no mpu is on the machine, so
X*	simulate mpu-401 (for debugging only)
X****************************************************************************/
X
Xvoid mpuexists(flag)
X    boolean flag;
X{
X    mpuflag = flag;
X}
X
X/*****************************************************************
X*			set_pitch_default
X*****************************************************************/
Xprivate void set_pitch_default()
X{
X    int i;
X
X    for (i = 0; i < 128; i++) {
X	pit_tab[i].pbend = 8192;
X	pit_tab[i].ppitch = i;
X    }
X}
X
X/*****************************************************************
X*			read_tuning
X*****************************************************************/
X
Xvoid read_tuning(filename)
X    char *filename;
X{
X    int index, pit, lineno = 0;
X    float bend;
X    FILE *fpp;
X
X    user_scale = true;
X    set_pitch_default();
X    fpp = fileopen(filename, "tun", "r", "Tuning definition file");
X    while ((fscanf(fpp, "%d %d %f\n", &index, &pit, &bend) > 2) &&
X	   (lineno < 128)) {
X	lineno++;
X	if (index >= -12 && index <= 115) {
X	    pit_tab[index+12].pbend = (int)(8192 * bend/100 + 8192);
X	    pit_tab[index+12].ppitch = pit;
X	}
X    }
X}
X
X
X/****************************************************************************
X*				   musicinit
X* Effect: 
X* Initialize the mpu 401 device driver
X*	Initialize mpu 401
X*		Reset 401, change defaults
X*	Set up interrupts
X*	Start up mpu record clock
X****************************************************************************/
X
Xvoid musicinit()
X{
X    int version, revision;
X    int i;
X    char *filename;
X
X    if (!tune_flag) {	/* do this code only once */
X	miditrace = (cl_nswitch(m_switches, n_m_sw) != NULL);
X	musictrace = (cl_nswitch(t_switches, n_t_sw) != NULL);
X
X    	tune_flag = true;
X	filename = cl_option("-tune");
X	if (filename != NULL) {
X	    read_tuning(filename);
X	}
X
X	intr_init();
X    }
X
X    last_cmd = 0;
X
X#ifdef TIMDIF
X	if (!initialized) settime();
X#endif
X
X    initialized = true;
X
X    intr_disable(IRQ);		/* Turn off 401 interrupts */
X
X    for (i = 0; i < 100; i++) { /* flush out buffer, ignore DSR */
X	inp(DATAPORT);
X    }
X
X    mpu_command(MC_RESET);	/* Reset the device */
X
X    mpu_command(MC_VERSION);
X    version = mpu_read();
X
X    mpu_command(MC_REVISION);
X    revision = mpu_read();
X
X    mpu_command(MC_SET_TEMPO);	/* Set tempo and timebase to get */
X    mpu_write(MD_BEATS_PER_MINUTE);	/* 400 ticks per second */
X
X    mpu_command(MC_TIMEBASE_192);
X
X    if (metroflag)
X	mpu_command(MC_ON_METRONOME);	/* Just for debugging */
X
X    mpu_command(MC_NO_MEASURE_END);	/* Don't want measure end bytes */
X
X    init_asm();			/* Do any asm init needed(aintr.asm)*/
X    intr_routine(IRQ);		/* Set up vector */ 
X
X    CBREAK = false;
X    setctl();			/* Set up ctrl-break intercept */
X
X#ifdef DEBUG
X    loop_max = 0;
X#endif
X    intr_enable(IRQ);		/* allow 401 interrupts */
X
X    if (user_scale) {
X	for (i = 0; i < num_voices; i++) {
X	    midi_bend(i, 8192);
X	    bend[i] = 8192;
X	}
X    }
X    midi_thru(!(cl_switch("-block")));	/* set MIDI thru on MPU-401 */
X
X    timereset();		/* Reset clock */
X}
X
X/****************************************************************************
X*				   musicterm
X* Effect: 
X*	Cleans up; disables MPU-401 interrupts; resets MIDI devices
X****************************************************************************/
X
Xvoid musicterm()
X{
X    if (initialized) {
X#ifdef TIMDIF
X	cletime();
X#endif
X	intr_disable(IRQ);	/* No more 401 interrupts */
X#ifdef DEBUG
X	printf("loop_max is %d\n", loop_max);
X#endif
X	clrctl();		/* reset ctrl-break handler */
X	intr_cleanup(IRQ);	/* Restore default vector */
X	mpu_command(MC_RESET);	/* Reset the device */
X	initialized = false;
X    }
X/*  printf("maximum successful wait for ack: %d\n", max_ack_wait);*/
X}
X
X/****************************************************************************
X*				    random
X* Inputs:
X*	int lo: Lower limit of value
X*	int hi: Upper limit of value
X* Result: int
X*	random number (lo <= result <= hi)
X****************************************************************************/
X
Xprivate long seed = 1534781;
X
Xint random(lo, hi)
X    int lo, hi;
X{
X    seed *= 13;
X    seed += 1874351;
X    return (int) (lo + 
X	    (((hi + 1 - lo) * ((0x00ffff00 & seed) >> 8)) >> 16));
X}
X
X/****************************************************************************
X*				   timereset
X* Effect: 
X*	Resets the time on the MPU-401.	 Ticks is reset to 0
X****************************************************************************/
X
Xvoid timereset()
X{
X    if (!initialized) fixup();
X    if (musictrace) printf("timereset()\n");
X    Ticks = 0;				/* Reset clock */
X    mpu_command(MC_START_RECORDING);	/* Starts up clock */
X#ifdef TIMDIF
X    cletime();
X    settime();
X#endif    
X}
X
X/****************************************************************************
X*			       trace
X* Inputs:
X*	boolean flag: true for trace on
X* Effect: 
X*	turns tracing on (flag == true) or off (flag == false)
X****************************************************************************/
Xvoid trace(flag)
X    boolean flag;
X{
X    musictrace = flag;
X}
X
X/****************************************************************************
X*			       tracemidi
X* Inputs:
X*	boolean flag: true for trace on
X* Effect: 
X*	turns midi tracing on (flag == true) or off (flag == false)
X****************************************************************************/
Xvoid tracemidi(flag)
X    boolean flag;
X{
X    miditrace = flag;
X}
X
X/****************************************************************************
X*			       trace_mpu_command
X* Inputs:
X*	int c: Command
X* Effect: 
X*	Writes command to stdout
X****************************************************************************/
X
Xprivate void trace_mpu_command(c)
X    int c;
X{
X    char * p;
X    char buf[10];
X
X    switch(c) { /* decode */
X	case MC_RESET:
X		p = " RESET:";
X		break;
X	case MC_ON_METRONOME:
X		p =" MET-ON:";
X		break;
X	case MC_SET_TEMPO:
X		p =" TEMPO:";
X		break;
X	case MC_TIMEBASE_192:
X		p =" TIME-192:";
X		break;
X	case MC_START_RECORDING:
X		p =" REC-ON:";
X		break;
X	case MC_VERSION:
X		p =" VERSION:";
X		break;
X	case MC_REVISION:
X		p =" REVISION:";
X		break;
X	case MC_NO_MEASURE_END:
X		p =" NO-MEAS-END:";
X		  break;
X	case MC_SND_MIDI:
X		p =" MIDI:";
X		break;
X	case MC_RECORD_COUNTER:
X		if (last_cmd == c) p = "#";
X		else p = " COUNTER:";
X		break;
X	case MC_ON_BEND:
X		p = "Bender:On";
X		break;
X	case MC_OFF_BEND:
X		p = "Bender:Off";
X		break;
X	case MC_ON_THRU:
X		p = "Thru:On";
X		break;
X	case MC_OFF_THRU:
X		p = "Thru:Off";
X		break;
X	default:sprintf(buf," %02x",c);
X		p = buf;
X		break;
X    } /* decode */
X
X    last_cmd = c;
X
X    if (len + strlen(p) > 70) { /* overflow */
X	printf("\n");
X	len = 0;
X    } /* overflow */
X    len += strlen(p);
X    printf("%s",p);
X}
X
X/****************************************************************************
X*				      wfa
X* Effect: 
X*	Waits for an acknowledgement from the MPU-401.	Will not wait more
X*	than MAX_ACK_WAIT iterations thru its loop.
X* Conditions:
X*	Called only if interrupts are not enabled, and the MPU-401 is
X*	being polled
X****************************************************************************/
X
X
Xprivate void wfa()
X{
X    int ackcnt;
X    int x;
X   
X    if (!mpuflag) return;
X
X    for (ackcnt = 0; ackcnt < MAX_ACK_WAIT; ackcnt++)
X	if ((x = mpu_read()) == ACK) break;
X#ifdef DEBUG
X	else printf("wfa: got %x; intcnt = %d\n", x, intcnt);
X#endif
X}
X
X
X/* From here my own routine begins */
X
Xal_nt_off()
X{
X	static	int	c;
X	for (c = 0; c < 16; c++)	{
X		mpu_write( 0xb0 + c);
X		mpu_write(123);
X		mpu_write(0);
X	}
X}
X
END_OF_FILE
if test 32980 -ne `wc -c <'mpu.c'`; then
    echo shar: \"'mpu.c'\" unpacked with wrong size!
fi
# end of 'mpu.c'
fi
echo shar: End of archive 6 \(of 6\).
cp /dev/null ark6isdone
MISSING=""
for I in 1 2 3 4 5 6 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 6 archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0
