Mac II Midi Demo
Volume Number: 3
Issue Number: 12
Column Tag: The Midi Mac
A Midi Demo for the Mac II 
By Kirk Austin, Contributing Editor, San Rafael, CA
Here we are back in MIDI land again. This is a continuation of the July 1987
article in which we bacame familiar with what MIDI is all about, and looked into some
of the low level routines that are necessary to work with MIDI on the Macintosh.
Now, probably what I didn’t tell you last time was that these low level routines
were designed to work with LightSpeed Pascal from Think Technologies. I have found
that this is the easiest development system for people just starting to program the
Macintosh because of its unique source level debugging features. Also, I have found the
Pascal language to be the best choice among languages available for the Macintosh
because the Macintosh was designed with the Pascal language in mind. Because of this
all of the documentation is written with a Pascal syntax (Inside Macintosh, Macintosh
Revealed, etc.).
As a result of this built-in bias, if any other language than Pascal is chosen as a
development tool, a great deal of time is typically spent just translating from the
Pascal documentation to whatever language you have decided to use. The moral of the
story is, if you are just starting out programming the Macintosh, you would be doing
yourself a big favor by choosing Pascal as your development language, ‘nuff said.
The Apple Music Fair
On July 10th Apple had an in-house party to let its employees find out more
about music programs for the Macintosh. It was a great party, with food and drinks in
the courtyard of the DeAnza 3 building. About a dozen or so companies with music
products for the Macintosh were present, showing their wares, and there was even a
presentation by Alan Kay on the future of computers and music.
I was impressed by the fact that Apple is making an effort to get its employees
excited about the musical possibilities of the Macintosh computer. The Mac has
become the defacto standard for MIDI controllers. If you attend one of the biannual
NAMM shows (which is where all of the new musical products are exhibited) you will
find that the Macintosh has taken over as far as musical computers go. I just wish
Apple would go a little bit further with their support of MIDI. For instance, I know
that there are MIDI routines built into the new ROM’s on the Macintosh II, but I can’t
get anyone at Apple to tell me what they are. Now, obviously, someone there knows
what the routines are, after all, someone had to write them in the first place, right?
But, for some reason, Apple is not releasing the information just yet. I hope this
changes soon, as I would like to be using ROM routines instead of having to write all of
my own code, but I guess I will just have to wait a while (sigh).
By the way, I heard that copies of the July issue of MacTutor are making the
rounds at Apple and I have gotten inquiries about the MIDI routines from some Apple
employees. Maybe I can stir up enough interest at Apple to get them to come through
with some information (are you listening, guys?).
Whoops!
Unfortunately, there was a slight oversight on my part in the program listings
that were printed in July that caused a bug in the interrupt handlers. If you are using
the low level routines in a program that uses input from the Macintosh keyboard, the
status register can become corrupted by the MIDI interrupt routines and the computer
will think that it is getting a never-ending string of keystrokes from the ASCII
keyboard. I get a string of lower case “c”, but othere people have reported getting
lower case “s”.
Anyway, the problem is that I neglected to save and restore the status register in
the interrupt routines, so you need to add the following two lines to the four interrupt
handlers (i.e. TxIntHandA, TxIntHandB, RxIntHandA, and RxIntHandB):
This should be the first line at the beginning of each routine:
MOVE SR,-(SP)
then, replace the line
ANDI #$F8FF,SR
at the end of each interrupt handler with the following line
MOVE (SP)+,SR
This change keeps the status register intact instead of changing its value after the
interrupt routine has executed. Sorry if this error has caused anyone a great deal of
hair pulling.
New Changes to LLMIDI
There are also a couple of additional routines that I have added to the library
since it was published. The revised routine library is available on the source code disk
for July, so if you just buy that you will save yourself an awful lot of typing. Also,
there is the distinct possibility that if you do type the listings in yourself that you will
make a typo and it will get flagged as an assembly error. The listings on the source
code disk have been assembled with MDS without any errors being flagged, so if you are
showing an error it is probably a typo.
Anyway, the new additions to the library have to do with filtering out “active
sensing” MIDI bytes from the data stream, and also adding a MIDI thru function that
echoes the incoming MIDI data on either the same port or the opposite one.
Active Sensing
This is a data byte that is sent out by some controllers every 300 milliseconds or
so that lets receiving equipment know that everything is hunky dory. Mostly, it just
gets in the way of whatever you might be trying to do with the MIDI data stream, so the
best thing to do is just filter it out before it gets placed in the buffer. A slight change
to the RxIntHand routines is all that is necessary to do this, and it consists of a grand
total of two lines of assembly code.
MIDI Thru
The MIDI thru capability is pretty easy to add too. It’s another change to the
RxIntHand routines that calls either TxMIDIA or TxMIDIB depending on the variable
ThruFlagA or ThruFlagB. In order to set the variables two routines had to be added to
the library: MIDIThruA and MIDIThruB.
The new routines
{1}
XDEF MIDIThruA
XDEF MIDIThruB
ThruFlagA DC 0 ; MIDI thru flag for modem port
ThruFlagB DC 0 ; MIDI thru flag for printer port
; This routine lets you do a MIDI Thru function
; The Thrucode is:
; 0 = No thru function
; 1 = MIDI thru on the same channel
; 2 = MIDI thru on the opposite channel
; Procedure MIDIThruA(Thrucode : integer);
MIDIThruA
LEA ThruFlagA,A0 ; point to the flag
MOVE 4(SP),(A0) ; set the flag
MOVE.L (SP)+,A0 ; save the return address
ADDQ #2,SP ; move past the parameter
MOVE.L A0,-(SP) ; put the return address back
RTS ; and return
; This is the interrupt routine for receiving through the
; modem port. It places the counter value and the MIDI byte in
; a circular queue to be accessed later by the application.
; When the system gets this far, A0 contains the SCC base read
; Ctl address and A1 contains the SCC base write Ctl address
; for this channel. The data addresses are offset by 4 from
; the control addresses. D0-D3/A0-A3 are already preserved, so
; they may be used freely.
RxIntHandA
MOVE SR,-(SP) ; save status register
ORI #$0300,SR ; disable interrupts
@3 MOVE #4,D0 ; get data offset
CLR.L D1 ; prepare for data
MOVE.L (SP),(SP) ; Delay
MOVE.B 0(A0,D0),D1 ; read data from SCC
MOVE.L (SP),(SP) ; Delay
CMPI #$FE,D1 ; filter out acitve sensing
BEQ @2
LEA ThruFlagA,A1 ;
CMPI #1,(A1) ; check for MIDI Thru
BNE @4
MOVE D1,-(SP) ; put data on the stack
BSR TxMIDIA ; send it out port A
@4
LEA ThruFlagA,A1 ;
CMPI #2,(A1) ; check for MIDI Thru
BNE @5
MOVE D1,-(SP) ; put data on the stack
BSR TxMIDIB ; send it out port B
@5
LEA RxQueueA,A2 ; point to queue
LEA RxByteInA,A3 ; get the address
MOVE (A3),D0 ; get offset to next cell
LEA Counter,A3 ; get the address
MOVE.L (A3),D2 ; put counter value in D2
LSL.L #8,D2 ; shift counter one byte
ADD.L D2,D1 ; combine counter and data
MOVE.L D1,0(A2,D0) ; put longword in queue
LEA RxQEmptyA,A3 ; get the address
MOVE #0,(A3) ; reset queue empty flag
ADDQ #4,D0 ; update index
CMP #$400,D0
BNE @1
MOVE #0,D0
@1 LEA RxByteInA,A3 ; get the address
MOVE D0,(A3)
@2 BTST.B #0,(A0) ; is there more data?
BNE @3 ; do it again if there is
MOVE (SP)+,SR ; restore status register
RTS ; and return
; This is the interrupt routine for transmitting a byte
; through the modem port. It checks to see if there is any
; data to send, and if there is it sends it to the SCC. If
; there isn’t it resets the TBE interrupt in the SCC and
; exits. When the system gets this far, A0 contains the SCC
; base read Ctl address and A1 contains the SCC base write Ctl
; address for this channel. The data addresses are offset by 4
; from the control addresses. D0-D3/A0-A3 are already pre
; served, so they may be used freely.
TxIntHandA
MOVE SR,-(SP) ; save the status register
ORI #$0300,SR ; disable interrupts
LEA TxQEmptyA,A3 ; get the address
TST.B (A3) ; Is queue empty?
BEQ @1 ; if not branch
MOVE.B #$28,(A1) ; if so, reset TBE interrupt
MOVE.L (SP),(SP) ; Delay
BRA TxIExitA ; and exit
@1 LEA TxByteOutA,A3 ; get the address
MOVE (A3),D0 ; get index to next data byte
LEA TxQueueA,A2 ; point to queue
MOVE #4,D1 ; get data offset
MOVE.B 0(A2,D0),0(A1,D1) ; write data to SCC
MOVE.L (SP),(SP) ; Delay
ADDQ #1,D0 ; update index
CMP #$100,D0
BNE @2
MOVE #0,D0
@2 LEA TxByteOutA,A3 ; get the address
MOVE D0,(A3)
LEA TxByteInA,A3 ; get the address
MOVE (A3),D1
CMP D0,D1 ; is TxQueue empty?