[comp.os.minix] Summary of responses on MIDI interrupt handling

hcj@lzaz.ATT.COM (HC Johnson) (01/07/89)

I Previously posted:

| I have tried, without success to implement an interrupt driven
| send to MIDI. (Receive is working fine.)
| 
| The problem area seems to be in getting the acia chip to pull down
| TXRDY, when I am down sending.

I received mail from two very helpful people:
Mike Russell ucbvax!pixar!mike	
And
John Stanley (dynasoft!john@stag.UUCP)

This work was done to produce a MIDI driver for MINIX/ST.

John wrote:

|  ..I'm not surprised you're having trouble with this, the docs available,
|even from the chip manufacturer, are in a word 'lousy'...  My sympathy if
|you're having to figure this out on your own...  I had to myself..  :)
|
|
|> Anyone out there have a code sample that shows how to use the
|> transmit interrupt??  Thanks.
|
|  Since I've done this, I can can assure you that it can be done.
|Unfortunately, since the code I wrote was while under contract to write a
|propriatary local area network, I can't send you source.  Sorry...
|
|  On the other hand, I can answer questions...  The 'trick' is to disable
|the TXRDY just before you toss the last character in the buffer out the
|port, not when you receive the next interrupt and have nothing left to
|send... it's too late at that point.  This also means that when you are
|loading the buffer, you have to enable TXRDY after you add a character to
|the buffer.
|
This is the Ultimate information on the crummy ACIA chip.  The info works.
The consequence to me is that the output of a singe byte cannot have an
interrupt.


Mike sent a program.  Considering John's info, I think this program works
by delaying until the last char is output before coercing the interrupts.


| Here you go:
| 
| 
| ---------------------- include file for Atari ST/Mega ACIA
| /* aciareg.h - definitions for Atari ST serial communications chip */
| 
| #define ACIA_MIDI (aciareg *)0xfffffc04
| typedef struct aciareg {
| 	char csr;
| 	char filler;
| 	char data;
| } aciareg;
| 
| /* control bits, write only */
| #define C_RESET		0x3		/* master reset */
| #define C_DIV1		0x0		/* divide clock by 1 */
| #define C_DIV16		0x1		/* divide clock by 16, normal for MIDI */
| #define C_DIV64		0x2		/* divide clock by 64 */
| 
| #define C_7EVEN2	0x00	/* seven bits, even parity, 2 stop bits */
| #define C_7ODD2	0x04	/* seven bits, odd  parity, 2 stop bits */
| #define C_7EVEN1	0x08	/* seven bits, even parity, 1 stop bit  */
| #define C_7ODD1	0x0c	/* seven bits, even parity, 1 stop bit  */
| 
| #define C_8NONE2	0x10	/* eight bits, no   parity, 2 stop bits */
| #define C_8NONE1	0x14	/* eight bits, no   parity, 1 stop bits */
| #define C_8EVEN1	0x18	/* eight bits, even parity, 1 stop bit  */
| #define C_8ODD1	0x1c		/* eight bits, odd  parity, 1 stop bit  */
| 
| #define	C_NO_RTS	0x40	/* disable RTS */
| #define C_TIENA	0x20		/* enable transmit interrupts */
| 							/* note: CTL_NO_RTS|CTL_TIENA sends a break */
| #define C_RIENA	0x80		/* enable reveive interrupts */
| 
| #define INIT_MIDI	(C_RIENA|C_8NONE1|C_DIV16)	/* init MIDI */
| #define INIT_KBD	(C_RIENA|C_8NONE1|C_DIV64)	/* init keyboard */
| 
| /* status bits, read only */
| #define S_RDATA		0x01	/* received data is present */
| #define S_XEMPTY	0x02	/* transmitter is ready */
| #define S_NOCARR	0x04	/* no carrier */
| #define S_CTS		0x08	/* no clear to send signal */
| #define S_FE		0x10	/* framing error */
| #define S_OVRN		0x20	/* reveive overrun */
| #define S_PE		0x40	/* parity error */
| #define S_IRQ		0x80	/* interrupt request */
| 
| #define S_ERROR		(S_FE|S_OVRN|S_PE)	/* a receive error has occurred */
| ---------------------- interrupt handler for Atari ST/Mega MIDI
| #include <osbind.h>
| #include <aciareg.h>
| 
| typedef int (func)();
| 
| static func *imidiin, *imidiout;	/* i/o interrupt handlers */
| static func *omidisys;				/* original midi vector */
| 
| static nullfunc() {}
| 
| midisys()	/* MIDI interrupt handler */
| {
| 	register aciareg *acia = ACIA_MIDI;
| 	char c;
| 	static xmitloop;	/* transmitter interrupt loop detector */
| #define XMITMAX 1000
| 	
| 	if(acia->csr&S_RDATA && (c = acia->data) != (char)0xfe) {
| 		(*imidiin)(c);
| 	}
| 	if(acia->csr&S_XEMPTY) {
| 		if(imidiout) {
| 			(*imidiout)();
| 			
| 			/* guard against transmitter interrupt loop */
| 			if(acia->csr&S_XEMPTY) {
| 				if(++xmitloop>XMITMAX)
| 					acia->csr = INIT_MIDI;
| 			} else {
| 				xmitloop = 0;
| 			}
| 		} else {
| 			acia->csr = INIT_MIDI;
| 		}
| 	}
| }
| 
| func *
| MidiISubr() { return imidiin; }	/* get current input interrupt func */
| 
| func *
| MidiOSubr() { return imidiout; } /* get current output interrupt func */
| 
| MidiSys(isubr, osubr)
| func *isubr;	/* MIDI input interrupt handler or zero */
| func *osubr;	/* MIDI output interrupt handler or zero */
| /*
| ** Set up the MIDI ACIA to call C function isubr for input interrupts
| ** and osubr() for output interrupts.  May be called while MIDI is
| ** active (but be ready for lost characters).  Call with both functions
| ** set to zero to reset the first existing MIDI vector.
| */
| {
| 	register aciareg *acia = ACIA_MIDI;
| 	register kbdvecs *kv = Kbdvbase();
| 	long osp;
| 	
| 	imidiin =  isubr?isubr:nullfunc;
| 	imidiout = osubr?osubr:nullfunc;
| 		
| 	if(isubr || osubr) {
| 		if(!omidisys)
| 			omidisys = kv->midisys;	/* save the original vector */
| 		kv->midisys = midisys;		/* replace it with our own */
| 	} else {
| 		if(omidisys)
| 			kv->midisys = omidisys;	/* restore the original vector */
| 	}
| 	
| 	osp = Super(0L);
| 	
| 	/* if output function specified, enable transmit interrupts */
| 	if(osubr)
| 		acia->csr = INIT_MIDI|C_TIENA;
| 	else
| 		acia->csr = INIT_MIDI;
| 	Super(osp);
| }
| #ifdef MAIN
| #include <stdio.h>
| int booby;
| ifunc()
| {
| 	booby++;
| }
| 
| main()
| {
| 	int obooby = 0;
| 		
| 	setbuf(stdout, NULL);
| 	MidiSys(ifunc, 0L);
| 	while(booby < 30) {
| 		if(obooby != booby)
| 			printf("%d\n", obooby = booby);
| 	}
| 	MidiSys(0L, 0L);
| }
| #endif MAIN

Finally, for you MINIX fans, here is the fix for handling the combined
MIDI and KBD interrupts that make TOS code so nasty:

/*===========================================================================*
 *				aciaint					     *
 *===========================================================================*/
PUBLIC aciaint()
{
	MFP->mf_ierb &= ~IB_AINT;
	do {
  if (KBD->ac_cs & A_IRQ)
	kbdint();
  if (MDI->ac_cs & A_IRQ)
	mdiint();
	} while ((MFP->mf_gpip & IO_AINT)==0);
	MFP->mf_ierb |= IB_AINT;
}

The secret is the mask of the ierb register, NOT the imrb.
Turning on the bit in the ierb restrobes the input and will produce
a new interrupt even if the request stays up.  

Thankyou again to Mike and John for their responses.

Howard Johnson
ATT-BL
lzaz!hcj