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