Apple II Technical Notes _____________________________________________________________________________ Developer Technical Support Apple IIgs #54: MIDI Drivers Revised by: Matt Deatherage November 1990 Written by: Jim Mensch May 1989 This Technical Note describes how to write a driver for use with the Apple IIgs MIDI tools. Changes since May 1989: Noted that MIDI drivers also work with the MIDI Synth tool. _____________________________________________________________________________ Apple ships two drivers with the MIDI tool set, APPLE.MIDI and CARD6850.MIDI, respectively. These drivers are adequate for almost all MIDI hardware currently on the market for the Apple IIgs; however, if your hardware is not compatible with either of these drivers, you have to write your own. This Note includes all the information you need to create a MIDI driver. Note that the same drivers that work with MIDI Tools (Tool #32) also work with the MIDI Synth (Tool #35). This Note collectively refers to MIDI Tools and MIDI Synth as the "MIDI tools." Purpose of the Driver and Description of Hardware Requirements The Apple MIDI tools communicate to the MIDI world via a simple driver. The driver's function is managing the transmission and reception of single bytes of MIDI data between the tools and the particular MIDI hardware involved. The MIDI tools operate on the assumption that the hardware has a method of interrupting the system when a character has been received and when a character can be transmitted. Since there is quite a bit of overhead in processing MIDI data, and since MIDI data can comes across a standard MIDI bus at a rate of over 3000 bytes per second, it is suggested that you provide a means for your device to buffer a few characters to reduce system overhead caused by interrupts if you are designing hardware to be used with the MIDI tools. Format of the Driver File The driver file is a standard OMF load file, which can be created with any of the popular Apple IIgs assemblers. The file must start with a dispatch table that contains the addresses of the standard driver routines. All driver routines must be in the same segment as the dispatch table. The dispatch table should have 13 four-byte entries, each of which contains the address of the appropriate routine minus one. Table 1 contains addresses of routines in the MIDI driver to perform specific functions. Call Function ______________________________________________________ Init Called to initialize the port and prime the driver ShutDown Called to close the port and clean up after driver Reset Called at reset time by the MIDI tools IntHandler Called when your interrupt occurs PollRecv Poll input the port for data RecvIntOn Turns on receiver interrupts RecvIntOff Turns off receiver interrupts PollXmit Polls the transmitter to see if another character can be sent XmitIntOn Enables transmitter interrupts XmitIntOff Disables transmitter interrupts NotImp Currently unused NotImp Currently unused NotImp Currently unused _____________________________________________________ Table 1-MIDI Driver Function Routines Routine Calling Conventions All driver routines are called with full 16-bit mode enabled and should exit the same way. On entry to each routine, the accumulator contains the direct page pointer that the driver should use if it wants to use the MIDI Tools' or MIDI Synth's direct page. It is the driver's responsibility to set the direct page register and restore it on exit. All other parameters are passed on the stack and should be removed from the stack before the routine exits. The MIDI tools set aside 128 bytes of space on the passed direct page for use by the driver. They are bytes $80-$FF. If you want to report an error inside of any routine (except IntHandler), set the carry flag on exit and load the accumulator with the error code. Use predefined error codes whenever possible. If you need to report a device specific error, use errors in the range $C0-$FF. The MIDI tools will set the high byte of the error code properly for you, so you do not need to do it yourself. Table 2 lists all of the potential predefined error codes. Error Code Error Definition _____________________________________________________________ miToolsErr ($2004) The required tools were not started miNoBufErr ($2007) No buffer is currently allocated miDevNotAvail ($2080) Requested device is not available miDevSlotBusy ($2081) Requested slot is already in use miDevBusy ($2082) Requested device is already in use miDevOverrun ($2083) Device overrun by incoming MIDI data miDevNoConnect ($2084) No connection to MIDI miDevReadErr ($2085) Framing error in received MIDI data miDevVersion ($2086) ROM version is incompatible with driver miDevIntHndlr ($2087) Conflicting interrupt handler installed _____________________________________________________________ Table 2-Predefined Eror Codes The Driver Routines Init This routine is called by the MIDI tools when it wants to initialize your port and tell the driver to prepare itself for the rest of the calls. Figure 1 shows how the stack looks on entry to this call. Figure 1-The Stack on Entry to Init The Init routine should first test to see if the port specified by SlotFlag and SlotNum is available for use. SlotNum is the number of the slot or the port that the user has requested for use, and SlotFlag indicates whether it is a built-in port or a card in a slot. After determining that the requested device is available, you should initialize the device, allocate any memory that your driver may require (beyond what is available in the direct page), and set the proper system interrupt vector to the address passed in NewIntAddr. Before setting the vector, be sure to save the old value, as the MIDI tools expect the result from this routine to be the old address stored in the vector. On exit, the stack should contain the return address and the old vector address. ShutDown This routine is called when the MIDI tools want your driver to release the MIDI device and prepare to be unloaded. Figure 2 shows how the stack looks on entry to this call. Figure 2-The Stack on Entry to ShutDown Your routine should change the interrupt vector that you used to OldIntVector. It should then deallocate all the memory that it allocated, disable all interrupts on the device, and if needed, tell the system that you are no longer using the port in question. Reset This routine is called when the system has been reset by the user. Figure 3 shows how the stack looks on entry to this call. Figure 3-The Stack on Entry to Reset All you should do at this point is attempt to deallocate any memory you were using and disable interrupts on the device you were using. Note: Do not set the interrupt vector to OldIntVector, instead remove the value from the stack and dispose of it. IntHandler The IntHandler routine is called by the MIDI tools when an interrupt occurs for the vector that you are using. The MIDI driver performs some setup then calls your routine. This routine does not have any parameters on the stack. Once called, your IntHandler routine should test the port to see if an interrupt has occurred on your device. If your device did not cause the interrupt, you should set the carry and exit as quickly as possible, reducing the system interrupt overhead. If your device caused the interrupt, you should test the receiver to see if any bytes of data are waiting to be read. If there is data waiting, you should load that data into the accumulator and perform a JSL to the following code: InBufGlue PEA $0400 PHD RTL This code calls the MIDI tools and tell them to accept the character in the accumulator into its input buffer. After accepting the data, control is passed back to the instruction following your JSL. If you received a byte of data and an error occurred during reception, you should load the number of the error code into the y register and perform a JSL to the following code: InErrGlue PEA $0500 PHD RTL Again, you will regain control right after the JSL. Once in your interrupt routine, you may perform the calls above for as much data as you like. For example, if your device has a three-byte buffer, you could call InBufGlue once for each waiting character, thus reducing your interrupt overhead and possibly preventing unneeded interrupts. If the transmitter on your device is ready to send data, you should perform a JSL to the following code: OutBufGlue PEA $8400 PHD RTL This routine will return with the carry set if no data is waiting to be transmitted or the carry clear if data is available. If data is waiting, the next character to send will be in the accumulator, and you should simply send it at that time. If no more data is available, you should disable transmitter interrupts and exit. The MIDI tools will re-enable transmitter interrupts the next time it has data to send. PollRecv The PollRecv (Poll Receive) routine is called by the MIDI tools every now and then to see if any data might be waiting to be read. There are no parameters on the stack for this call. Your driver should test to see if any data is available and transmit it all to the MIDI tools via the InBufGlue described in the IntHandler description. PollXmit The PollXmit (Poll Transmit) routine is called by the MIDI tools when any data is added to the MIDI output buffer. There are no parameters on the stack for this routine. Your driver should enable transmitter interrupts, test to see if it can send any data immediately, and if it can, call OutBufGlue as described int the IntHandler description to get data to send. XmitIntOn and RecvIntOn These routines are called when the MIDI tools want to explicitly enable transmitter or receiver interrupts. They have no parameters on the stack and should, when called, enable transmitter interrupts for XmitIntOn and receiver interrupts for RecvIntOn. XmitIntOff and RecvIntOff These routine are called when the MIDI tools want to explicitly disable transmitter or receiver interrupts. They have no parameters on the stack and should, when called, disable transmitter interrupts for XmitIntOff and receiver interrupts for RecvIntOff. NotImp These routines are not yet implemented, but your driver should be ready to handle a call to them. When called, they should clear the accumulator, clear the carry and perform an RTL back to the MIDI tools. A MIDI Driver Skeleton You can use the following sample code as a basis for a MIDI driver. It is not a complete driver in itself, and you will need to add code where comments with asterisks (***) appear for it to be functional. This example is in MPW IIgs assembler format. ****************************************************************************** * MIDI.DRVR.Aii * * (C) Copyright Apple Computer, Inc. 1988 * All rights reserved. * * by Don Marsh & Jim Mensch * 10/26/88 * * This is a shell that can be used to create custom MIDI drivers for use with * the Apple MIDI tool set. This shell is not functional, but can be used as a * starting point for creating your own custom MIDI drivers. * * Files: System Macros and equates * * * * Modification History: * * Version 1.0 Mensch * * 10/26/88 * * Create first draft * ****************************************************************************** Include 'E16.MIDI' Include 'M16.MiscTool' Include 'E16.MiscTool' Include 'M16.util' ; ; Direct page usage Note: ; MIDI drivers may use the upper half ($80-$FF) of the MIDI direct page. When ; a MIDI driver routine is called the Accumulator will contain the direct page ; pointer for the MIDI tool set. If your driver requires more storage than ; 128 bytes, it will have to allocate them itself using the memory manager. theuserID equ $80 ; location to store the passed user ID PortInUse equ theuserID+2 ; storage for the port number in use deref equ PortInUse+2 Temp equ Deref+4 EJECT ************************************************************ ******************* * DispatchTable RECORD * * Description: Every MIDI Driver must start with a driver dispatch table * that contains the entry point minus 1 of each of the * required entry points. * * * Inputs: None * * Outputs: None * * External Refs: Import DRVRInit Import DRVRShutDown Import DRVRReset Import DRVRIntHandler Import DRVRPollRecv Import DRVRRecvIntOn Import DRVRRecvIntOff Import DRVRPollXmit Import DRVRXmitIntOn Import DRVRXmitIntOff Import DRVRNotImplemented * * Entry Points: None * ************************************************************ ******************* DC.L DRVRInit DC.L DRVRShutDown DC.L DRVRReset DC.L DRVRIntHandler DC.L DRVRPollRecv DC.L DRVRRecvIntOn DC.L DRVRRecvIntOff DC.L DRVRPollXmit DC.L DRVRXmitIntOn DC.L DRVRXmitIntOff DC.L DRVRNotImplemented DC.L DRVRNotImplemented DC.L DRVRNotImplemented ; a few of the routines will need a temporary storage location that can be used ; even after the direct page is set back to what it was, This is a good place ; to put it! ErrorCode ds.W 1 ; temporary holder of an error code EndR EJECT ************************************************************ ******************* * DRVRInit PROC * * Description: This is called by the MIDI Tools when it needs to Init * your MIDI Driver. This is usually in response to a MIDIxxx * call made by the application. * When this routine is called, you should allocate any buffer * space that you will need beyond the direct page, you should * enable the interrupts on your MIDI Device, and then set the * appropriate system interrupt vector and return the old vector * value. If the init works fine, clear the carry and return. * If an error occurs return the appropriate error code * in the Accumulator, and set the carry. * * * Inputs: UserID:Word ID of application, for mem allocation * SlotFlag:Word 0 for internal port/ 1 for slot * SlotNum:Word number of slot/port to use * NewIntVector:Long address to give system as its new * interrupt vector. This routine is in the * MIDI tool set, and it performs needed * setup before it calls your interrupt * routine * * Outputs: OldIntVector:Long Address interrupt vector used to have * * External Refs: None * * Entry Points: None * ************************************************************ ******************* ; Offsets for parameters on the stack ProcStatus equ 1 OldDPage equ ProcStatus+1 ReturnAddress equ OldDPage+2 UserID equ ReturnAddress+3 SlotFlag equ UserID+2 SlotNum equ SlotFlag+2 NewIntVector equ SlotNum+2 OldIntVector equ NewIntVector+4 ParmBytes equ 10 ParmEnd equ ReturnAddress+ParmBytes ; first disable interrupts since we are going to be setting up interrupt vectors ; and enabling interrupt generating hardware. We wouldn't want an interrupt to go ; off before we were ready to handle it! Then set us up to use the MIDI direct ; page. php ; save the old proc status phd ; save the old direct page tcd ; Set Direct page to the one passed SEI ; and disable interrupts ; now get the user ID and save it, and allocate any buffers that we may need ; Since most drivers will never need more than 128 bytes of storage we will ; not allocate any storage space lda UserID,s ; first save the user ID for later sta theUserID ; in our section of the MIDI DPage ; *** Insert any memory allocation needed here *** ; Next, you should check the slot flag and number to see if they are compatible ; with this driver. If they are, you should continue and initialize the proper ; port. If they are not proper, you should exit with an error. ; For this example, I will be testing the SlotFlag, to see if it is set to ; external. lda SlotFlag,s ; first test the slot flag to be sure bne FlagOK ; its non-zero. ldy #miDevNotAvail ; if its zero, signal not available bra InitError ; and exit via error routine FlagOK lda SlotNum,s ; Now save the slot number in sta PortInUse ; our data area ; *** At this point you should test the firmware in the desired slot to be sure ; that the card you want is properly installed, if it is not then you should ; pass back the appropriate error *** ; Now that you know that you have the proper slot information and you have tested ; to be sure that you have the hardware needed for the driver it is time for you ; to initialize the interface and to enable its interrupts. ; *** Install code to initialize your hardware/interrupts here *** ; Now that the Port has been properly initialized, you must set up the proper ; system interrupt vector. Since we required an external card above it would ; make sense that you need to use the "Other unspecified interrupt handler" ; vector (Number $0017). But first, remember to get the original vector pointer ; because we must return it to the MIDI tools. PushLong #0 ; space for result PushWord #otherIntHnd ; vector to retrieve _GetVector ; and get the vector in question PullLong Temp ; place in storage for a sec lda Temp ; now place it on the stack sta OldIntVector ; as the result of this function lda Temp+2 sta OldIntVector+2 lda NewIntVector ; now move the MIDI Interrupt routine sta Temp ; pointer into temporary storage lda NewIntVector+2 sta Temp+2 PushWord #otherIntHnd ; now set the vector to point to PushLong Temp ; the MIDI drivers interrupt routine _SetVector ; The driver is now all set up, pull off the passed parms and we are done! Done ldy #0 ; set the error code to 0. No error ; ; This is the alternate label for the Done routine that should be called when ; an error has occurred. InitError lda ReturnAddress,s ; Move the return address below the sta ParmEnd,s ; parameters lda ReturnAddress+1,s sta ParmEnd+1,s pld ; get the direct page back plp ; get the processor status back tsc ; now adjust the stack pointer sec ; so that the parameters are gone sbc #ParmBytes tcs ; now the return address is on Top tya ; put any error into cmp #1 ; set the carry if non-zero RTL ; and return EndP EJECT ************************************************************ ******************* * DRVRShutDown PROC * * Description: This routine will be called whenever the MIDI Tools want * to cause your driver to let go of the port it was using. * * * Inputs: OldIntVector:Long Address to place back into the system * interrupt vector you were using * * Outputs: Carry clear if successful * Carry set if not, error in * * External Refs: Import DrvrRecvIntOff Import DRVRXMitIntOff * * Entry Points: * ************************************************************ ******************* With DispatchTable ProcStatus equ 1 OldDPage equ ProcStatus+1 ReturnAddress equ OldDPage+2 OldIntVector equ ReturnAddress+3 ParmBytes equ 4 ParmEnd equ ReturnAddress+ParmBytes ; first disable interrupts since we are going to be setting up interrupt vectors ; We wouldn't want an interrupt to go off before we were ready to handle it! ; Then set us up to use the MIDI direct page. php ; save the old proc status phd ; save the old direct page tcd ; Set Direct page to the one passed SEI ; and disable interrupts lda #0 ; zero out the temp error code sta >ErrorCode ; Now First, re-install the old interrupt vector lda OldIntVector ; get the old vector off the stack sta Temp ; and save it in globals for a sec lda OldIntVector+2 sta Temp+2 PushWord #otherIntHnd ; now set the vector to point to PushLong Temp ; its original routine. _SetVector ; Next, turn off the interface hardware, and tell it to stop generating ; interrupts. We can share some code here and call our DRVRRecvIntOff and ; DRVRXmitIntOff routines. Always remember load the direct page into the ; accumulator. tdc ; get direct page into jsl DRVRXmitIntOff ; and turn off transmitter interrupts tdc jsl DRVRRecvIntOff ; and now receiver interrupts. ; *** Usually turning off interrupts will be all that you would need to do at ; this point, however, if your interface card requires extra shutdown code ; this is where you would place it *** ; *** If you allocated any memory in the DRVRInit call, this is the place to ; get rid of it. ; If an error were to occur in this routine, you should simply store the error ; number in our temporary error code variable like this ; ; lda #ErrorNumber ; Sta >ErrorCode Done ; Now that we are done shutting down the driver, pull off the passed data ; and end. pld ; first retrieve the old dpage plp ; and processor status Longa Off ; next move the return address SEP #$20 ; we need a short acc for this trick pla ; pull the 3 byte return address ply ; into and plx ; now remove the remaining bytes plx ; of passed parameters phy ; and restore the return address pha Longa On REP #$30 ; and turn back on full 16-bit mode lda >ErrorCode ; retrieve the error code cmp #1 ; and set the carry if non-zero RTL EndP EJECT ************************************************************ ******************* * DRVRReset PROC * * Description: This routine will be called whenever MIDIReset is called. * and that should only happen when an actual reset occurred. * It should in most cases perform the exact same functions * as MIDI Shutdown. * * * Inputs: OldIntVector:Long Original contents of interrupt vector * * Outputs: None * * External Refs: * * Entry Points: * ************************************************************ ******************* jmp DRVRShutDown EndP EJECT ************************************************************ ******************* * DRVRIntHandler PROC * * Description: This routine is the very core of the MIDI driver. It takes * care of passing data back and forth between the MIDI tools * and your hardware. It will be called for both input and * output. * * * Inputs: None * * Outputs: Carry set if interrupt not serviced * * External Refs: Import DRVRXmitIntOff * * Entry Points: Export InBufGlue Export InErrGlue Export OutBufGlue * ************************************************************ ******************* phd ; first, save the current dpage tcd ; and use the MIDI DPage ; The first thing the interrupt routine should do is to test to see if the ; interrupt was actually generated by our port. If it was then we should handle ; it, but if not, we should simply exit this routine with the carry set as ; fast as we can, so that the next interrupt handler will get it in a timely ; manner. ; *** Insert code here to test to see if the original interrupt was yours *** beq ServicePort ; if it was our, handle it ; If the interrupt was not ours, set the carry and leave pld ; restore the direct page sec rtl ServicePort ; the interrupt was ours, continue ; This routine should test the interrupt again, too see if the port is ready ; to transmit or receive, If it is ready to transmit or receive, it should ; then call the ServiceRecv, or ServiceXMit routines ; *** Insert code here to test for receive bne ServiceRecv ; if chars waiting try receive it ; If no more characters are waiting, see if we are ready to transmit any ; characters. bne ServiceXMit ; if can send a character do it ; If both the above tests fail, then exit the interrupt handler for now pld ; restore the direct page clc ; clear the carry to indicate serviced RTL ; and return ; The following routine ServiceRecv will be called when a character is waiting ; It should retrieve that character, pass it to the MIDI drivers, and then ; branch back to the beginning of ServicePort, to see if any more chars are ; waiting. ServiceRecv ; *** Place code here that retrieves a byte of data from the port *** ; Call MIDI tools this way if no error has occurred on receive ( contains the ; data read) RecvOK jsl InBufGlue ; call the MIDI tools bra ServicePort ; and check for more data in or out ; Call MIDI this way if a reception error has occurred ( contains the ; data read) RecvErr ldy #miDevReadErr ; load Y with the error jsl InErrGlue ; call the midi tools bra ServicePort ; The routine ServiceXmit will be called when the port is ready to send data. ; it will actually call the MIDI tools and get a character to send. ServiceXmit jsl OutBufGlue ; call the MIDI tools for the next char bcs NoMoreData ; if the carry set then no data to send ; *** at this point the byte to transmit is in , place your code to output ; it thru the port here *** ; Now that the data has been sent, you can either loop thru ServicePort again, ; or you could simply end and wait for the next interrupt to send another ; character. This sample will simply exit at this point bra Done ; after sending the character end. ; NoMoreData is called when the MIDI Tools said that they did not have any more ; data to transmit, so we should turn off transmitter interrupts at this point ; in case our device likes to keep interrupting if its empty. NoMoreData phd ; push the direct page reg on the stack jsl DRVRXmitIntOff ; enable xmit interrupts Done pld ; restore the DPage clc ; signal the interrupt as handled rtl ; and get outta here! ; The routine inbufglue should be called when you received a character from your ; port with no error and you want to pass it to the MIDI tools. InBufGlue pea $0400 ; push on the long address of the phd ; direct page and a proc status byte RTL ; and jump back to the MIDI tools ; The routine inErrGlue should be called when you received a character from your ; port and an error has occurred. In this case, it should still be passed to the ; MIDI driver, as it may still be useful inErrGlue pea $0500 ; push on the long address of the phd ; direct page and a proc status byte RTL ; and jump back to the MIDI tools ; The routine OutBufGlue should be called when you are ready to send a char ; out your port. The MIDI tools will will return with the character to send ; in . If the MIDI tools have no more characters to send then OutBufGlue ; will return with the carry set. OutBufGlue pea $8400 ; push on the long address of the phd ; direct page and a proc status byte RTL ; and jump back to the MIDI tools EndP EJECT ************************************************************ ******************* * DRVRPollRecv PROC * * Description: This routine is called by the MIDI tools when it wants to * pool the port for data instead of waiting for an interrupt. * its function is similar to that of the our interrupt handler * except that it only does input. * * Inputs: None * * Outputs: Carry set if interrupt not serviced * * External Refs: Import InBufGlue Import InErrGlue * * Entry Points: None * ************************************************************ ******************* phd ; first, save the current dpage tcd ; and use the MIDI DPage php SEI ServicePort ; the interrupt was ours, continue ; This routine should test the port too see if the port has any data for use ; to receive. If it does, it calls the MIDI tools and hands it off. Also note ; this routine will turn off interrupts, since we wouldn't want any stray ; receiver interrupts to spoil our fun and grab the data from us. (This is ; very important for certain types of ports which may signal that the port ; is ready and the generate an interrupt, thus leaving us in a situation where ; our interrupt routines could steal the interrupt right out from under us before ; we fetched it, thus allowing us to possibly double post a character. ; *** Insert code here to test for received data *** bne ServiceRecv ; if chars waiting try receive it ; If no more data is waiting exit this routine. plp pld ; restore the direct page clc ; clear the carry no errors possible RTL ; and return ; The following routine ServiceRecv will be called when a character is waiting ; It should retrieve that character, pass it to the MIDI drivers, and then ; branch back to the beginning of ServicePort, to see if any more chars are ; waiting. ServiceRecv ; *** Place code here that retrieves a byte of data from the port *** ; Call MIDI tools this way if no error has occurred on receive ( contains the ; data read) RecvOK jsl InBufGlue ; call the MIDI tools bra ServicePort ; and check for more data in or out ; Call MIDI this way if a reception error has occurred ( contains the ; data read) RecvErr ldy #miDevReadErr ; load Y with the error jsl InErrGlue ; call the midi tools bra ServicePort EndP EJECT ************************************************************ ******************* * DRVRPollXMit PROC * * Description: This routine is called when the MIDI tools wants to start * an output stream. The tool set calls this routine for the * first character of data, and then this routine is * responsible for enabling transmitter interrupts and sending * the character. * * * Inputs: None * * Outputs: Carry set if interrupt not serviced * * External Refs: None Import OutBufGlue Import DRVRXmitIntOn * * Entry Points: None * ************************************************************ ******************* phd ; first, save the current dpage tcd ; and use the MIDI DPage php ; disable interrupts as we are now going SEI ; to turn on xmitter interrupts. ; First see if the port is ready to send any data, if not simply exit ; *** Insert code here to test if output is ready *** bcs Done ; if not, then simply end ; The port is ready to accept a character for output so, call MIDI tools ; to get the next character jsl OutBufGlue ; get the next character bcs Done ; if carry set, no chars to xmit so end pha ; save the character to send phd ; push the direct page reg on the stack jsl DRVRXmitIntOn ; enable xmit interrupts pla ; retrieve the character to send ; *** Insert code here to transmit a character *** Done plp ; get the old interrupt status pld ; get the old direct page lda #0 ; no errors are possible clc rtl EndP EJECT ************************************************************ ******************* * DRVRXmitIntOn PROC * * Description: This routine will be called when the MIDI tools need to * enable transmitter interrupts on your device. * * * Inputs: None * * Outputs: None * * External Refs: * * Entry Points: * ************************************************************ ******************* php ; save proc status/interrupt state phd ; save the old direct page tcd ; use the MIDI tools DPage SEI ; disable interrupts ; *** Insert code here to enable transmitter interrupts on your device pld ; recover old direct page plp ; recover old interrupt state lda #0 ; and return no-error (none possible) clc rtl EndP ************************************************************ ******************* * DRVRXmitIntOff PROC * * Description: This routine will be called when the MIDI tools need to * Disable transmitter interrupts on your device. * * * Inputs: None * * Outputs: None * * External Refs: * * Entry Points: * ************************************************************ ******************* php ; save proc status/interrupt state phd ; save the old direct page tcd ; use the MIDI tools DPage SEI ; disable interrupts ; *** Insert code here to Disable transmitter interrupts on your device pld ; recover old direct page plp ; recover old interrupt state lda #0 ; and return no-error (none possible) clc rtl EndP EJECT ************************************************************ ******************* * DRVRRecvIntOn PROC * * Description: This routine will be called when the MIDI tools need to * enable receiver interrupts on your device. * * * Inputs: None * * Outputs: None * * External Refs: * * Entry Points: * ************************************************************ ******************* php ; save proc status/interrupt state phd ; save the old direct page tcd ; use the MIDI tools DPage SEI ; disable interrupts ; *** Insert code here to enable receiver interrupts on your device pld ; recover old direct page plp ; recover old interrupt state lda #0 ; and return no-error (none possible) clc rtl EndP ************************************************************ ******************* * DRVRRecvIntOff PROC * * Description: This routine will be called when the MIDI tools need to * Disable receiver interrupts on your device. * * * Inputs: None * * Outputs: None * * External Refs: * * Entry Points: * ************************************************************ ******************* php ; save proc status/interrupt state phd ; save the old direct page tcd ; use the MIDI tools DPage SEI ; disable interrupts ; *** Insert code here to Disable receiver interrupts on your device pld ; recover old direct page plp ; recover old interrupt state lda #0 ; and return no-error (none possible) clc rtl EndP ************************************************************ ******************* * DRVRNotImplemented PROC * * Description: Dummy routine, should leave the stack alone and return * no error * * * Inputs: None * * Outputs: None * * External Refs: * * Entry Points: * ************************************************************ ******************* lda #0 clc RTL EndP END Further Reference: o Apple IIgs Toolbox Reference Update