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.

Note: For some reason, the figures are missing from this Note. This is not a formatting problem, but rather a defecit of the text file as available online. I apologise for any inconvenience or confusion. -- AH

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 routines 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:


This and all of the other Apple II Technical Notes have been converted to HTML by Aaron Heiss as a public service to the Apple II community, with permission by Apple Computer, Inc. Any and all trademarks, registered and otherwise, are properties of their owners.