# Enclosed is the source for driving the Macintosh MIDI interface
# designed by Steve Falco (moss!saf).  Send him mail for info on
# building the interface.
#
###### (cut here) ######
# This is a shell archive.  Remove anything before this line, then
# unpack it by saving it in a file and typing "sh file".  (Files
# unpacked will be owned by you and have default permissions.)
#
# This archive contains:
# README makefile mdr.a mdr.lst midi.h t2.c

echo x - README
cat > "README" << '//E*O*F README//'
The code in this archive is all based on the Apple MPW Development System.
Because of potential syntax differences with other development systems,
particularly regarding assembler macros, I have included a copy of the
assembler output listing so you can see how to expand the macros by hand
if necessary.  Note that the MPW assembler does code-optimization, so in
several cases the code generated doesn't match the source.  Such lines are
tagged with a G in the listing.  Also, supervisor-mode instructions are
tagged with a P.

The makefile is really for information only.  You can't use it on Unix
because MPW doesn't run on Unix.  You can't use it on the mac either because
MPW uses a few non-ASCII characters in its makefiles (like option-f) which
I altered in order to move the file to Unix.  To convert it back to MPW format,
change all colons (:) to option-f and change the asterisk (*) to option-x.

	Steve Falco
	moss!saf
//E*O*F README//

echo x - makefile
cat > "makefile" << '//E*O*F makefile//'
t2		:	t2.c.o mdr.a.o
	link -l -la -lf t2.c.o mdr.a.o {clibraries}* -o t2 > mapit

t2.c.o	:	t2.c
	c t2.c
	
mdr.a.o	:	mdr.a
	asm -f -font monaco,9 -l mdr.a 
//E*O*F makefile//

echo x - mdr.a
cat > "mdr.a" << '//E*O*F mdr.a//'
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; midi driver
;
; Steven A. Falco  
; 3/6/87
;
; taken from article in nov 85 mactutor by Kirk Austin
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
		BRANCH	SHORT		; use short displacements
		CASE	ON		; be case-sensitive
		STRING	PASCAL		; use pascal-style strings
;
; queue lengths
;
TxQSize		EQU	$1000		; transmit buffer length
RxQSize		EQU	$1000		; receive buffer length
;
; queue state flags
;
midi_em		EQU	$FF		; queue is empty
midi_ne		EQU	$00		; queue is not empty
;
; The following are stack offsets given that an item is passed
; by the MPW C compiler and LINK/UNLK is used. DANGER!!!  MPW C
; always passes in a long value.
;
; Note that in Pascal, a byte is passed as a short rather than a long...
; so Get_Byte would be $9.  Also, in C, the caller cleans up, while in
; Pascal the callee cleans up.
;
Get_Byte	EQU	$000B
Get_Short	EQU	$000A
Get_Long	EQU	$0008
;
; select one based on oscillator frequency used in hardware
;
;C_baud		EQU	%01000100	; 500 KHz
C_baud		EQU	%10000100	; 1 MHz
;C_baud		EQU	%11000100	; 2 MHz
;
; misc controls in SCC chip
;
No_W_Req	EQU	0
Rx_off		EQU	%11000000
Tx_off		EQU	%01100010
TRxC		EQU	%00101000
Brgen_off	EQU	0
Rx_on		EQU	%11000001
Tx_on		EQU	%01101010
DCD_int		EQU	%00001000
RstExtSt	EQU	%00010000
EnInt		EQU	%00010011
MIntEn		EQU	%00001010
EnMoInt		EQU	%00000001
No_Vec		EQU	%00000010
aReset		EQU	%10000000
rstTBE		EQU	$28
rstEERR		EQU	$30
NoData		EQU	$FFFF
;
; misc offsets in trap table
;
arxIO		EQU	24
atxIO		EQU	16
aspRC		EQU	28
;
; SR masks for interrupt control
;
DisInt		EQU	$0300
EnaInt		EQU	$F8FF
;
; SCC registers
;
SCC0		EQU	0
SCC1		EQU	1
SCC2		EQU	2
SCC3		EQU	3
SCC4		EQU	4
SCC5		EQU	5
SCC6		EQU	6
SCC7		EQU	7
SCC8		EQU	8
SCC9		EQU	9
SCC10		EQU	10
SCC11		EQU	11
SCC12		EQU	12
SCC13		EQU	13
SCC14		EQU	14
SCC15		EQU	15
;
; macro to enqueue a byte to be transmitted (can't use a subroutine
; 'cause we expect the stack to be in a certain state!)
;
		MACRO
		TxEnqueue
		MOVE	TxByteIn(A5),D0		; where to put it (offset)
		LEA	TxQueue(A5),A2		; base of queue
		MOVE.B	Get_Byte(A6),0(A2,D0)	; copy it from the stack to the queue
		ADDQ	#1,D0			; bump offset
		CMP	#TxQSize,D0		; did it wrap?
		BNE	@1			; nope
		MOVE	#0,D0			; you betcha'
@1		MOVE	D0,TxByteIn(A5)		; replace it
		ENDM
;
; macro for delay (SCC is slow compared to 68000)
;
		MACRO
		Delay
		MOVE.L	(SP),(SP)
		ENDM
;
; macro to load an SCC register (double moves are on purpose - slow down)
;
		MACRO
		Lscc	&sreg,&val
		MOVE.B	&sreg,D0
		MOVE.B	D0,(A0)
		Delay
		MOVE.B	&val,D0
		MOVE.B	D0,(A0)
		Delay
		ENDM
;
; debug traps for Macsbug
;
Dbgb		OPWORD	$A9FF		; breakpoint
Dbgs		OPWORD	$ABFF		; print string and breakpoint
;
; debug macro (currently commented out)
;
		MACRO
		Dbs	&string
;;;;;;;;;;;;	PEA	&string
;;;;;;;;;;;;	Dbgs
		ENDM
;
; chip addresses and offsets
;
		PRINT	OFF
		INCLUDE	'SysEqu.a'
		PRINT	ON
;
; this code must always be resident so we load it in Main segment to
; guarantee that.  (Ever call an interrupt handler that wasn't resident?)
;
		SEG	'Main'
;
; global area
;
		EXPORT (midi_txst, midi_rxst):DATA
;
CtlOffset	DS.W	1			; channel control offset
DataOffset	DS.W	1			; channel data offset
ChnReset	DS.B	1			; SCC channel reset select
RxIntOffset	DS.W	1			; for dispatch table
TxIntOffset	DS.W	1			; likewise
SpecRecCond	DS.W	1			; and one more
;
TxQueue		DS.B	TxQSize			; Tx data queue
midi_txst	DS.B	1			; the queue state
TxByteIn	DS.W	1			; offset for enqueue
TxByteOut	DS.W	1			; offset for dequeue
;
RxQueue		DS.B	RxQSize			; and the Rx queue
midi_rxst	DS.B	1			; the queue state
RxByteIn	DS.W	1			; offset for enqueue
RxByteOut	DS.W	1			; offset for dequeue
;
; interrupt handler for received characters
;
RxIntHand	PROC	ENTRY
		ORI	#DisInt,SR		; wipe out those interrupts
		MOVEM.L	D0-D1/A0-A2/A5,-(SP)	; very important...
		MOVE.L	CurrentA5,A5		; we need the globals
		MOVE.L	SCCRd,A0		; hardware addresses
		MOVE.L	SCCWr,A1
;
		Dbs	#'rx interrupt'
;
		MOVE	DataOffset(A5),D0	; ... offset
		MOVE.B	0(A0,D0),D1		; read the data - it's here given the interrupt
		Delay
		LEA	RxQueue(A5),A2		; base of destination queue
		MOVE	RxByteIn(A5),D0		; ... offset
		MOVE.B	D1,0(A2,D0)		; store it
		MOVE.B	#midi_ne,midi_rxst(A5)	; guaranteed not empty
		ADDQ	#1,D0			; we filled one
		CMP	#RxQSize,D0		; wrapped it?
		BNE	@1			; not yet
		MOVE	#0,D0			; yes - reset to top
;
@1		MOVE	D0,RxByteIn(A5)		; replace the offset
;
		MOVEM.L	(SP)+,D0-D1/A0-A2/A5	; restore context
		ANDI	#EnaInt,SR		; once again interruptible
		RTS				; back to user
		ENDP
;
; interrupt handler for transmitted characters
;
TxIntHand	PROC	ENTRY
		ORI	#DisInt,SR		; you leave me alone
		MOVEM.L	D0-D1/A0-A2/A5,-(SP)	; and I'll leave you alone
		MOVE.L	CurrentA5,A5
		MOVE.L	SCCRd,A0		; read base
		MOVE.L	SCCWr,A1		; write base
;
		Dbs	#'tx interrupt'
;
		TST.B	midi_txst(A5)		; is there something to write?
		BEQ	@1			; no - pity
		MOVE	CtlOffset(A5),D0	; offset to control
		MOVE.B	#rstTBE,0(A1,D0)	; we saw the empty...
		Delay
		BRA	TxIExit			; nothing to be done
;
@1		MOVE	TxByteOut(A5),D0	; offset of something to send
		LEA	TxQueue(A5),A2		; base of queue
		MOVE	DataOffset(A5),D1	; offset to hardware
		MOVE.B	0(A2,D0),0(A1,D1)	; move it out
		Delay
		ADDQ	#1,D0			; we used it up
		CMP	#TxQSize,D0		; wrapped?
		BNE	@2			; no...
		MOVE	#0,D0			; 'fraid so
;
@2		MOVE	D0,TxByteOut(A5)	; remember position
		MOVE	TxByteIn(A5),D1		; see about the input side
		CMP	D0,D1			; now empty?
		BNE	TxIExit			; no - just exit
		MOVE.B	#midi_em,midi_txst(A5)	; it's empty
;
TxIExit
		MOVEM.L	(SP)+,D0-D1/A0-A2/A5	; be polite
		ANDI	#EnaInt,SR		; allow interrupts
		RTS
		ENDP
;
; a stub for special conditions - we sort-of handle them
;
Stub		PROC	ENTRY
		ORI	#DisInt,SR
		MOVEM.L	A0-A1/A5,-(SP)
		MOVE.L	CurrentA5,A5
		MOVE.L	SCCRd,A0		; read base
		MOVE.L	SCCWr,A1		; write base
;
		Dbs	#'stub interrupt'
;
		MOVE	CtlOffset(A5),D0	; offset to control
		MOVE.B	#rstEERR,0(A1,D0)	; we saw the botch, clear it
		Delay
		MOVEM.L	(SP)+,A0-A1/A5
		ANDI	#EnaInt,SR
		RTS
		ENDP
;
; init routine - must call this first to set up stuff!
;
midi_init	PROC	EXPORT
		MOVE	#aCtl,CtlOffset(A5)
		MOVE	#aData,DataOffset(A5)
		MOVE.B	#aReset,ChnReset(A5)
		MOVE	#arxIO,RxIntOffset(A5)
		MOVE	#atxIO,TxIntOffset(A5)
		MOVE	#aspRC,SpecRecCond(A5)
;
		MOVE	SR,-(SP)		; save interrupts
		MOVEM.L	D0/A0-A1,-(SP)		; save registers
		ORI	#DisInt,SR		; interrupts off		
;
		Dbs	#'init routine'
;
		MOVE.L	SCCRd,A1		; get base address
		ADD	CtlOffset(A5),A1	; ... control
		MOVE.B	(A1),D0			; dummy read to clear SCC WR0
		Delay
;
; set up SCC channel A
;
		MOVE.L	SCCWr,A0		; another base address
		ADD	CtlOffset(A5),A0	; ... control
		Lscc	#SCC9,ChnReset(A5)	; reset channel
		Lscc	#SCC4,#C_baud		; set clock rate
		Lscc	#SCC1,#No_W_Req		; no write request
		Lscc	#SCC3,#Rx_off		; turn it off
		Lscc	#SCC5,#Tx_off		; him too
		Lscc	#SCC9,#No_Vec		; not vectored interrupts
		Lscc	#SCC11,#TRxC		; set clock source
		Lscc	#SCC14,#Brgen_off	; no break gen
		Lscc	#SCC3,#Rx_on		; now turn channel back on
		Lscc	#SCC5,#Tx_on		; these on too
		Lscc	#SCC15,#DCD_int		; Mr. Mousie on
		Lscc	#SCC0,#RstExtSt		; reset ext status
		Lscc	#SCC0,#RstExtSt		; reset ext status
		Lscc	#SCC1,#EnInt		; enable interrupts
		Lscc	#SCC9,#MIntEn		; master interrupt
;
; install trap vectors
;
		MOVE.L	#Lvl2DT,A0		; dispatch table
		MOVE	RxIntOffset(A5),D0	; ... offset
		LEA	RxIntHand,A1		; pointer to handler
		MOVE.L	A1,0(A0,D0)		; load the pointer into the dispatch table
		MOVE	TxIntOffset(A5),D0	; ... offset
		LEA	TxIntHand,A1		; another pointer
		MOVE.L	A1,0(A0,D0)		; load it
		MOVE	SpecRecCond(A5),D0	; ... offset
		LEA	Stub,A1			; dummy handler
		MOVE.L	A1,0(A0,D0)		; load it
;
; clear queues
;
		CLR	RxByteIn(A5)		; clear circ. buffer pointers
		CLR	RxByteOut(A5)
		MOVE.B	#midi_em,midi_rxst(A5)	; mark it empty
		CLR	TxByteIn(A5)
		CLR	TxByteOut(A5)
		MOVE.B	#midi_em,midi_txst(A5)	; mark it empty
;
		MOVEM.L	(SP)+,D0/A0-A1		; restore registers
		MOVE	(SP)+,SR		; interrupts on
		RTS
		ENDP
;
; midi_tx - send a byte to the midi port (we expect the stack to contain a long)
;
midi_tx		PROC	EXPORT
		LINK	A6,#0			; remember where our argument is
		MOVE	SR,-(SP)		; save interrupts
		MOVEM.L	D0/A0-A2,-(SP)		; and some registers
		ORI	#DisInt,SR		; interrupts off
;
		Dbs	#'transmit routine'
;
		TST.B	midi_txst(A5)		; check the flag
		BNE	TxQE			; queue empty - send direct
		; something here - just add to queue
		TxEnqueue
		BRA	TxExit			; go home	
;
TxQE		; must write directly to device (maybe)
		MOVE.L	SCCRd,A0		; read base
		MOVE.L	SCCWr,A1		; write base
		MOVE	CtlOffset(A5),D0	; ... offset
		BTST.B	#txBE,0(A0,D0)		; is the device ready
		BNE	FirstByte		; yes - do direct write
		; device busy - add to queue - interrupt will wake up
		; handler and send it out when the device is free
		TxEnqueue
		MOVE.B	#midi_ne,midi_txst(A5)	; now it's not empty
		BRA	TxExit			; done
;
FirstByte	; it really wants this byte...
		MOVE	DataOffset(A5),D0	; ... offset
		Delay
		MOVE.B	Get_Byte(A6),0(A1,D0)	; copy it from the stack to the device
		Delay
;
TxExit		; clean up the world
		MOVEM.L	(SP)+,D0/A0-A2		; restore registers
		MOVE	(SP)+,SR		; and interrupts
		UNLK	A6
		RTS				; in C, the caller cleans up
		ENDP
;
; midi_rx - get a byte from the port (leave it in D0 as a long)
; We return -1 if nothing is available
;
midi_rx		PROC	EXPORT
		; don't need link 'cause nothing is passed in
		MOVE	SR,-(SP)		; save the interrupts
		MOVEM.L	D1-D2/A0-A2,-(SP)	; and some registers
		ORI	#DisInt,SR		; turn off the interrupts
;
		Dbs	#'receive routine'
;
		TST.B	midi_rxst(A5)		; any data available?
		BEQ	@1			; yes
		MOVE.W	#NoData,D0		; no-data flag
		EXT.L	D0			; sign-extend it
		BRA	RxExit			; C returns stuff in D0
;
@1		MOVE	RxByteOut(A5),D2	; ... offset
		LEA	RxQueue(A5),A2		; and base
		CLR.L	D0			; start off empty
		MOVE.B	0(A2,D2),D0		; get the byte
		ADDQ	#1,D2			; up the offset
		CMP	#RxQSize,D2		; wrapped around?
		BNE	@2			; not yet
		MOVE	#0,D2			; yes - reset it to the top
;
@2		MOVE	D2,RxByteOut(A5)	; and save it
		MOVE	RxByteIn(A5),D1		; now look at the input side
		CMP	D2,D1			; is the queue now empty?
		BNE	RxExit			; no - don't worry about it
		MOVE.B	#midi_em,midi_rxst(A5)	; yes - set the flag
;
RxExit
		MOVEM.L	(SP)+,D1-D2/A0-A2	; restore registers
		MOVE	(SP)+,SR		; and interrupts
		RTS
		ENDP
;
; reset the modem port - must call before quitting because the trap
; vectors will be invalid once we exit!!!
;
midi_reset	PROC	EXPORT
		MOVE	#aCtl,CtlOffset(A5)	; prepare globals
		MOVE	#aData,DataOffset(A5)
		MOVE.B	#aReset,ChnReset(A5)
		MOVE	#arxIO,RxIntOffset(A5)
		MOVE	#atxIO,TxIntOffset(A5)
		MOVE	#aspRC,SpecRecCond(A5)
;
		MOVE	SR,-(SP)		; save interrupts
		MOVE.L	A0,-(SP)
		ORI	#DisInt,SR		; disable interrupts
;
		Dbs	#'reset routine'
;
		MOVE.L	SCCWr,A0		; base for writing
		ADD	CtlOffset(A5),A0	; and the offset
;
		Lscc	#SCC9,ChnReset(A5)	; reset channel
		Lscc	#SCC15,#DCD_int		; Mr. Mousie stays on
		Lscc	#SCC0,#RstExtSt		; reset ext status
		Lscc	#SCC0,#RstExtSt		; reset ext status
		Lscc	#SCC1,#EnMoInt		; enable mouse interrupts
		Lscc	#SCC9,#MIntEn		; master interrupt
;
; We don't turn the vectors back to where they were...
; We also don't leave interrupts enabled so it's OK
;
		MOVE.L	(SP)+,A0		; restore register
		MOVE	(SP)+,SR		; interrupts on
		RTS
		ENDP
;
		END
//E*O*F mdr.a//

echo x - mdr.lst
cat > "mdr.lst" << '//E*O*F mdr.lst//'
                               	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
                               	;
                               	; midi driver
                               	;
                               	; Steven A. Falco
                               	; 3/6/87
                               	;
                               	; taken from article in nov 85 mactutor by Kirk Austin
                               	;
                               	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
                               	;
                               			BRANCH	SHORT		; use short displacements
                               			CASE	ON		; be case-sensitive
                               			STRING	PASCAL		; use pascal-style strings
                               	;
                               	; queue lengths
                               	;
        0000 1000              	TxQSize		EQU	$1000		; transmit buffer length
        0000 1000              	RxQSize		EQU	$1000		; receive buffer length
                               	;
                               	; queue state flags
                               	;
        0000 00FF              	midi_em		EQU	$FF		; queue is empty
        0000 0000              	midi_ne		EQU	$00		; queue is not empty
                               	;
                               	; The following are stack offsets given that an item is passed
                               	; by the MPW C compiler and LINK/UNLK is used. DANGER!!!  MPW C
                               	; always passes in a long value.
                               	;
                               	; Note that in Pascal, a byte is passed as a short rather than a long...
                               	; so Get_Byte would be $9.  Also, in C, the caller cleans up, while in
                               	; Pascal the callee cleans up.
                               	;
        0000 000B              	Get_Byte	EQU	$000B
        0000 000A              	Get_Short	EQU	$000A
        0000 0008              	Get_Long	EQU	$0008
                               	;
                               	; select one based on oscillator frequency used in hardware
                               	;
                               	;C_baud		EQU	%01000100	; 500 KHz
        0000 0084              	C_baud		EQU	%10000100	; 1 MHz
                               	;C_baud		EQU	%11000100	; 2 MHz
                               	;
                               	; misc controls in SCC chip
                               	;
        0000 0000              	No_W_Req	EQU	0
        0000 00C0              	Rx_off		EQU	%11000000
        0000 0062              	Tx_off		EQU	%01100010
        0000 0028              	TRxC		EQU	%00101000
        0000 0000              	Brgen_off	EQU	0
        0000 00C1              	Rx_on		EQU	%11000001
        0000 006A              	Tx_on		EQU	%01101010
        0000 0008              	DCD_int		EQU	%00001000
        0000 0010              	RstExtSt	EQU	%00010000
        0000 0013              	EnInt		EQU	%00010011
        0000 000A              	MIntEn		EQU	%00001010
        0000 0001              	EnMoInt		EQU	%00000001
        0000 0002              	No_Vec		EQU	%00000010
        0000 0080              	aReset		EQU	%10000000
        0000 0028              	rstTBE		EQU	$28
        0000 0030              	rstEERR		EQU	$30
        0000 FFFF              	NoData		EQU	$FFFF
                               	;
                               	; misc offsets in trap table
                               	;
        0000 0018              	arxIO		EQU	24
        0000 0010              	atxIO		EQU	16
        0000 001C              	aspRC		EQU	28
                               	;
                               	; SR masks for interrupt control
                               	;
        0000 0300              	DisInt		EQU	$0300
        0000 F8FF              	EnaInt		EQU	$F8FF
                               	;
                               	; SCC registers
                               	;
        0000 0000              	SCC0		EQU	0
        0000 0001              	SCC1		EQU	1
        0000 0002              	SCC2		EQU	2
        0000 0003              	SCC3		EQU	3
        0000 0004              	SCC4		EQU	4
        0000 0005              	SCC5		EQU	5
        0000 0006              	SCC6		EQU	6
        0000 0007              	SCC7		EQU	7
        0000 0008              	SCC8		EQU	8
        0000 0009              	SCC9		EQU	9
        0000 000A              	SCC10		EQU	10
        0000 000B              	SCC11		EQU	11
        0000 000C              	SCC12		EQU	12
        0000 000D              	SCC13		EQU	13
        0000 000E              	SCC14		EQU	14
        0000 000F              	SCC15		EQU	15
                               	;
                               	; macro to enqueue a byte to be transmitted (can't use a subroutine
                               	; 'cause we expect the stack to be in a certain state!)
                               	;
                               			MACRO
                               			TxEnqueue
                               			MOVE	TxByteIn(A5),D0		; where to put it (offset)
                               			LEA	TxQueue(A5),A2		; base of queue
                               			MOVE.B	Get_Byte(A6),0(A2,D0)	; copy it from the stack to the queue
                               			ADDQ	#1,D0			; bump offset
                               			CMP	#TxQSize,D0		; did it wrap?
                               			BNE	@1			; nope
                               			MOVE	#0,D0			; you betcha'
                               	@1		MOVE	D0,TxByteIn(A5)		; replace it
                               			ENDM
                               	;
                               	; macro for delay (SCC is slow compared to 68000)
                               	;
                               			MACRO
                               			Delay
                               			MOVE.L	(SP),(SP)
                               			ENDM
                               	;
                               	; macro to load an SCC register (double moves are on purpose - slow down)
                               	;
                               			MACRO
                               			Lscc	&sreg,&val
                               			MOVE.B	&sreg,D0
                               			MOVE.B	D0,(A0)
                               			Delay
                               			MOVE.B	&val,D0
                               			MOVE.B	D0,(A0)
                               			Delay
                               			ENDM
                               	;
                               	; debug traps for Macsbug
                               	;
        A9FF                   	Dbgb		OPWORD	$A9FF		; breakpoint
        ABFF                   	Dbgs		OPWORD	$ABFF		; print string and breakpoint
                               	;
                               	; debug macro (currently commented out)
                               	;
                               			MACRO
                               			Dbs	&string
                               	;;;;;;;;;;;;	PEA	&string
                               	;;;;;;;;;;;;	Dbgs
                               			ENDM
                               	;
                               	; chip addresses and offsets
                               	;
                               			PRINT	ON
                               	;
                               	; this code must always be resident so we load it in Main segment to
                               	; guarantee that.  (Ever call an interrupt handler that wasn't resident?)
                               	;
                               			SEG	'Main'
                               	;
                               	; global area
                               	;
                               			EXPORT (midi_txst, midi_rxst):DATA
                               	;
00000                          	CtlOffset	DS.W	1			; channel control offset
00000                          	DataOffset	DS.W	1			; channel data offset
00000                          	ChnReset	DS.B	1			; SCC channel reset select
00000                          	RxIntOffset	DS.W	1			; for dispatch table
00000                          	TxIntOffset	DS.W	1			; likewise
00000                          	SpecRecCond	DS.W	1			; and one more
                               	;
00000                          	TxQueue		DS.B	TxQSize			; Tx data queue
00000                          	midi_txst	DS.B	1			; the queue state
00000                          	TxByteIn	DS.W	1			; offset for enqueue
00000                          	TxByteOut	DS.W	1			; offset for dequeue
                               	;
00000                          	RxQueue		DS.B	RxQSize			; and the Rx queue
00000                          	midi_rxst	DS.B	1			; the queue state
00000                          	RxByteIn	DS.W	1			; offset for enqueue
00000                          	RxByteOut	DS.W	1			; offset for dequeue
                               	;
                               	; interrupt handler for received characters
                               	;
00000                          	RxIntHand	PROC	ENTRY
00000 P 007C 0300              			ORI	#DisInt,SR		; wipe out those interrupts
00004   48E7 C0E4              			MOVEM.L	D0-D1/A0-A2/A5,-(SP)	; very important...
00008 G 2A78 0904              			MOVE.L	CurrentA5,A5		; we need the globals
0000C G 2078 01D8              			MOVE.L	SCCRd,A0		; hardware addresses
00010 G 2278 01DC              			MOVE.L	SCCWr,A1
00014                          	;
00014                          			Dbs	#'rx interrupt'
00014                         1	;;;;;;;;;;;;	PEA	&string
00014                         1	;;;;;;;;;;;;	Dbgs
00014                          	;
00014   302D 0000              			MOVE	DataOffset(A5),D0	; ... offset
00018   1230 0000              			MOVE.B	0(A0,D0),D1		; read the data - it's here given the interrupt
0001C                          			Delay
0001C   2E97                  1			MOVE.L	(SP),(SP)
0001E   45ED 0000              			LEA	RxQueue(A5),A2		; base of destination queue
00022   302D 0000              			MOVE	RxByteIn(A5),D0		; ... offset
00026   1581 0000              			MOVE.B	D1,0(A2,D0)		; store it
0002A G 422D 0000              			MOVE.B	#midi_ne,midi_rxst(A5)	; guaranteed not empty
0002E   5240                   			ADDQ	#1,D0			; we filled one
00030 G 0C40 1000              			CMP	#RxQSize,D0		; wrapped it?
00034   6602            00038  			BNE	@1			; not yet
00036 G 4240                   			MOVE	#0,D0			; yes - reset to top
00038                          	;
00038   3B40 0000              	@1		MOVE	D0,RxByteIn(A5)		; replace the offset
0003C                          	;
0003C   4CDF 2703              			MOVEM.L	(SP)+,D0-D1/A0-A2/A5	; restore context
00040 P 027C F8FF              			ANDI	#EnaInt,SR		; once again interruptible
00044   4E75                   			RTS				; back to user
00046                          			ENDP
                               	;
                               	; interrupt handler for transmitted characters
                               	;
00000                          	TxIntHand	PROC	ENTRY
00000 P 007C 0300              			ORI	#DisInt,SR		; you leave me alone
00004   48E7 C0E4              			MOVEM.L	D0-D1/A0-A2/A5,-(SP)	; and I'll leave you alone
00008 G 2A78 0904              			MOVE.L	CurrentA5,A5
0000C G 2078 01D8              			MOVE.L	SCCRd,A0		; read base
00010 G 2278 01DC              			MOVE.L	SCCWr,A1		; write base
00014                          	;
00014                          			Dbs	#'tx interrupt'
00014                         1	;;;;;;;;;;;;	PEA	&string
00014                         1	;;;;;;;;;;;;	Dbgs
00014                          	;
00014   4A2D 0000              			TST.B	midi_txst(A5)		; is there something to write?
00018   670E            00028  			BEQ	@1			; no - pity
0001A   302D 0000              			MOVE	CtlOffset(A5),D0	; offset to control
0001E   13BC 0028 0000         			MOVE.B	#rstTBE,0(A1,D0)	; we saw the empty...
00024                          			Delay
00024   2E97                  1			MOVE.L	(SP),(SP)
00026   6030            00058  			BRA	TxIExit			; nothing to be done
00028                          	;
00028   302D 0000              	@1		MOVE	TxByteOut(A5),D0	; offset of something to send
0002C   45ED 0000              			LEA	TxQueue(A5),A2		; base of queue
00030   322D 0000              			MOVE	DataOffset(A5),D1	; offset to hardware
00034   13B2 0000 1000         			MOVE.B	0(A2,D0),0(A1,D1)	; move it out
0003A                          			Delay
0003A   2E97                  1			MOVE.L	(SP),(SP)
0003C   5240                   			ADDQ	#1,D0			; we used it up
0003E G 0C40 1000              			CMP	#TxQSize,D0		; wrapped?
00042   6602            00046  			BNE	@2			; no...
00044 G 4240                   			MOVE	#0,D0			; 'fraid so
00046                          	;
00046   3B40 0000              	@2		MOVE	D0,TxByteOut(A5)	; remember position
0004A   322D 0000              			MOVE	TxByteIn(A5),D1		; see about the input side
0004E   B240                   			CMP	D0,D1			; now empty?
00050   6606            00058  			BNE	TxIExit			; no - just exit
00052   1B7C 00FF 0000         			MOVE.B	#midi_em,midi_txst(A5)	; it's empty
00058                          	;
00058                          	TxIExit
00058   4CDF 2703              			MOVEM.L	(SP)+,D0-D1/A0-A2/A5	; be polite
0005C P 027C F8FF              			ANDI	#EnaInt,SR		; allow interrupts
00060   4E75                   			RTS
00062                          			ENDP
                               	;
                               	; a stub for special conditions - we sort-of handle them
                               	;
00000                          	Stub		PROC	ENTRY
00000 P 007C 0300              			ORI	#DisInt,SR
00004   48E7 00C4              			MOVEM.L	A0-A1/A5,-(SP)
00008 G 2A78 0904              			MOVE.L	CurrentA5,A5
0000C G 2078 01D8              			MOVE.L	SCCRd,A0		; read base
00010 G 2278 01DC              			MOVE.L	SCCWr,A1		; write base
00014                          	;
00014                          			Dbs	#'stub interrupt'
00014                         1	;;;;;;;;;;;;	PEA	&string
00014                         1	;;;;;;;;;;;;	Dbgs
00014                          	;
00014   302D 0000              			MOVE	CtlOffset(A5),D0	; offset to control
00018   13BC 0030 0000         			MOVE.B	#rstEERR,0(A1,D0)	; we saw the botch, clear it
0001E                          			Delay
0001E   2E97                  1			MOVE.L	(SP),(SP)
00020   4CDF 2300              			MOVEM.L	(SP)+,A0-A1/A5
00024 P 027C F8FF              			ANDI	#EnaInt,SR
00028   4E75                   			RTS
0002A                          			ENDP
                               	;
                               	; init routine - must call this first to set up stuff!
                               	;
00000                          	midi_init	PROC	EXPORT
00000   3B7C 0002 0000         			MOVE	#aCtl,CtlOffset(A5)
00006   3B7C 0006 0000         			MOVE	#aData,DataOffset(A5)
0000C   1B7C 0080 0000         			MOVE.B	#aReset,ChnReset(A5)
00012   3B7C 0018 0000         			MOVE	#arxIO,RxIntOffset(A5)
00018   3B7C 0010 0000         			MOVE	#atxIO,TxIntOffset(A5)
0001E   3B7C 001C 0000         			MOVE	#aspRC,SpecRecCond(A5)
00024                          	;
00024 P 40E7                   			MOVE	SR,-(SP)		; save interrupts
00026   48E7 80C0              			MOVEM.L	D0/A0-A1,-(SP)		; save registers
0002A P 007C 0300              			ORI	#DisInt,SR		; interrupts off
0002E                          	;
0002E                          			Dbs	#'init routine'
0002E                         1	;;;;;;;;;;;;	PEA	&string
0002E                         1	;;;;;;;;;;;;	Dbgs
0002E                          	;
0002E G 2278 01D8              			MOVE.L	SCCRd,A1		; get base address
00032 G D2ED 0000              			ADD	CtlOffset(A5),A1	; ... control
00036   1011                   			MOVE.B	(A1),D0			; dummy read to clear SCC WR0
00038                          			Delay
00038   2E97                  1			MOVE.L	(SP),(SP)
0003A                          	;
0003A                          	; set up SCC channel A
0003A                          	;
0003A G 2078 01DC              			MOVE.L	SCCWr,A0		; another base address
0003E G D0ED 0000              			ADD	CtlOffset(A5),A0	; ... control
00042                          			Lscc	#SCC9,ChnReset(A5)	; reset channel
00042   103C 0009             1			MOVE.B	#SCC9,D0
00046   1080                  1			MOVE.B	D0,(A0)
00048                         1			Delay
00048   2E97                  2			MOVE.L	(SP),(SP)
0004A   102D 0000             1			MOVE.B	ChnReset(A5),D0
0004E   1080                  1			MOVE.B	D0,(A0)
00050                         1			Delay
00050   2E97                  2			MOVE.L	(SP),(SP)
00052                          			Lscc	#SCC4,#C_baud		; set clock rate
00052   103C 0004             1			MOVE.B	#SCC4,D0
00056   1080                  1			MOVE.B	D0,(A0)
00058                         1			Delay
00058   2E97                  2			MOVE.L	(SP),(SP)
0005A   103C 0084             1			MOVE.B	#C_baud,D0
0005E   1080                  1			MOVE.B	D0,(A0)
00060                         1			Delay
00060   2E97                  2			MOVE.L	(SP),(SP)
00062                          			Lscc	#SCC1,#No_W_Req		; no write request
00062   103C 0001             1			MOVE.B	#SCC1,D0
00066   1080                  1			MOVE.B	D0,(A0)
00068                         1			Delay
00068   2E97                  2			MOVE.L	(SP),(SP)
0006A G 4200                  1			MOVE.B	#No_W_Req,D0
0006C   1080                  1			MOVE.B	D0,(A0)
0006E                         1			Delay
0006E   2E97                  2			MOVE.L	(SP),(SP)
00070                          			Lscc	#SCC3,#Rx_off		; turn it off
00070   103C 0003             1			MOVE.B	#SCC3,D0
00074   1080                  1			MOVE.B	D0,(A0)
00076                         1			Delay
00076   2E97                  2			MOVE.L	(SP),(SP)
00078   103C 00C0             1			MOVE.B	#Rx_off,D0
0007C   1080                  1			MOVE.B	D0,(A0)
0007E                         1			Delay
0007E   2E97                  2			MOVE.L	(SP),(SP)
00080                          			Lscc	#SCC5,#Tx_off		; him too
00080   103C 0005             1			MOVE.B	#SCC5,D0
00084   1080                  1			MOVE.B	D0,(A0)
00086                         1			Delay
00086   2E97                  2			MOVE.L	(SP),(SP)
00088   103C 0062             1			MOVE.B	#Tx_off,D0
0008C   1080                  1			MOVE.B	D0,(A0)
0008E                         1			Delay
0008E   2E97                  2			MOVE.L	(SP),(SP)
00090                          			Lscc	#SCC9,#No_Vec		; not vectored interrupts
00090   103C 0009             1			MOVE.B	#SCC9,D0
00094   1080                  1			MOVE.B	D0,(A0)
00096                         1			Delay
00096   2E97                  2			MOVE.L	(SP),(SP)
00098   103C 0002             1			MOVE.B	#No_Vec,D0
0009C   1080                  1			MOVE.B	D0,(A0)
0009E                         1			Delay
0009E   2E97                  2			MOVE.L	(SP),(SP)
000A0                          			Lscc	#SCC11,#TRxC		; set clock source
000A0   103C 000B             1			MOVE.B	#SCC11,D0
000A4   1080                  1			MOVE.B	D0,(A0)
000A6                         1			Delay
000A6   2E97                  2			MOVE.L	(SP),(SP)
000A8   103C 0028             1			MOVE.B	#TRxC,D0
000AC   1080                  1			MOVE.B	D0,(A0)
000AE                         1			Delay
000AE   2E97                  2			MOVE.L	(SP),(SP)
000B0                          			Lscc	#SCC14,#Brgen_off	; no break gen
000B0   103C 000E             1			MOVE.B	#SCC14,D0
000B4   1080                  1			MOVE.B	D0,(A0)
000B6                         1			Delay
000B6   2E97                  2			MOVE.L	(SP),(SP)
000B8 G 4200                  1			MOVE.B	#Brgen_off,D0
000BA   1080                  1			MOVE.B	D0,(A0)
000BC                         1			Delay
000BC   2E97                  2			MOVE.L	(SP),(SP)
000BE                          			Lscc	#SCC3,#Rx_on		; now turn channel back on
000BE   103C 0003             1			MOVE.B	#SCC3,D0
000C2   1080                  1			MOVE.B	D0,(A0)
000C4                         1			Delay
000C4   2E97                  2			MOVE.L	(SP),(SP)
000C6   103C 00C1             1			MOVE.B	#Rx_on,D0
000CA   1080                  1			MOVE.B	D0,(A0)
000CC                         1			Delay
000CC   2E97                  2			MOVE.L	(SP),(SP)
000CE                          			Lscc	#SCC5,#Tx_on		; these on too
000CE   103C 0005             1			MOVE.B	#SCC5,D0
000D2   1080                  1			MOVE.B	D0,(A0)
000D4                         1			Delay
000D4   2E97                  2			MOVE.L	(SP),(SP)
000D6   103C 006A             1			MOVE.B	#Tx_on,D0
000DA   1080                  1			MOVE.B	D0,(A0)
000DC                         1			Delay
000DC   2E97                  2			MOVE.L	(SP),(SP)
000DE                          			Lscc	#SCC15,#DCD_int		; Mr. Mousie on
000DE   103C 000F             1			MOVE.B	#SCC15,D0
000E2   1080                  1			MOVE.B	D0,(A0)
000E4                         1			Delay
000E4   2E97                  2			MOVE.L	(SP),(SP)
000E6   103C 0008             1			MOVE.B	#DCD_int,D0
000EA   1080                  1			MOVE.B	D0,(A0)
000EC                         1			Delay
000EC   2E97                  2			MOVE.L	(SP),(SP)
000EE                          			Lscc	#SCC0,#RstExtSt		; reset ext status
000EE G 4200                  1			MOVE.B	#SCC0,D0
000F0   1080                  1			MOVE.B	D0,(A0)
000F2                         1			Delay
000F2   2E97                  2			MOVE.L	(SP),(SP)
000F4   103C 0010             1			MOVE.B	#RstExtSt,D0
000F8   1080                  1			MOVE.B	D0,(A0)
000FA                         1			Delay
000FA   2E97                  2			MOVE.L	(SP),(SP)
000FC                          			Lscc	#SCC0,#RstExtSt		; reset ext status
000FC G 4200                  1			MOVE.B	#SCC0,D0
000FE   1080                  1			MOVE.B	D0,(A0)
00100                         1			Delay
00100   2E97                  2			MOVE.L	(SP),(SP)
00102   103C 0010             1			MOVE.B	#RstExtSt,D0
00106   1080                  1			MOVE.B	D0,(A0)
00108                         1			Delay
00108   2E97                  2			MOVE.L	(SP),(SP)
0010A                          			Lscc	#SCC1,#EnInt		; enable interrupts
0010A   103C 0001             1			MOVE.B	#SCC1,D0
0010E   1080                  1			MOVE.B	D0,(A0)
00110                         1			Delay
00110   2E97                  2			MOVE.L	(SP),(SP)
00112   103C 0013             1			MOVE.B	#EnInt,D0
00116   1080                  1			MOVE.B	D0,(A0)
00118                         1			Delay
00118   2E97                  2			MOVE.L	(SP),(SP)
0011A                          			Lscc	#SCC9,#MIntEn		; master interrupt
0011A   103C 0009             1			MOVE.B	#SCC9,D0
0011E   1080                  1			MOVE.B	D0,(A0)
00120                         1			Delay
00120   2E97                  2			MOVE.L	(SP),(SP)
00122   103C 000A             1			MOVE.B	#MIntEn,D0
00126   1080                  1			MOVE.B	D0,(A0)
00128                         1			Delay
00128   2E97                  2			MOVE.L	(SP),(SP)
0012A                          	;
0012A                          	; install trap vectors
0012A                          	;
0012A G 307C 01B2              			MOVE.L	#Lvl2DT,A0		; dispatch table
0012E   302D 0000              			MOVE	RxIntOffset(A5),D0	; ... offset
00132   43FA 0000              			LEA	RxIntHand,A1		; pointer to handler
00136   2189 0000              			MOVE.L	A1,0(A0,D0)		; load the pointer into the dispatch table
0013A   302D 0000              			MOVE	TxIntOffset(A5),D0	; ... offset
0013E   43FA 0000              			LEA	TxIntHand,A1		; another pointer
00142   2189 0000              			MOVE.L	A1,0(A0,D0)		; load it
00146   302D 0000              			MOVE	SpecRecCond(A5),D0	; ... offset
0014A   43FA 0000              			LEA	Stub,A1			; dummy handler
0014E   2189 0000              			MOVE.L	A1,0(A0,D0)		; load it
00152                          	;
00152                          	; clear queues
00152                          	;
00152   426D 0000              			CLR	RxByteIn(A5)		; clear circ. buffer pointers
00156   426D 0000              			CLR	RxByteOut(A5)
0015A   1B7C 00FF 0000         			MOVE.B	#midi_em,midi_rxst(A5)	; mark it empty
00160   426D 0000              			CLR	TxByteIn(A5)
00164   426D 0000              			CLR	TxByteOut(A5)
00168   1B7C 00FF 0000         			MOVE.B	#midi_em,midi_txst(A5)	; mark it empty
0016E                          	;
0016E   4CDF 0301              			MOVEM.L	(SP)+,D0/A0-A1		; restore registers
00172 P 46DF                   			MOVE	(SP)+,SR		; interrupts on
00174   4E75                   			RTS
00176                          			ENDP
                               	;
                               	; midi_tx - send a byte to the midi port (we expect the stack to contain a long)
                               	;
00000                          	midi_tx		PROC	EXPORT
00000   4E56 0000              			LINK	A6,#0			; remember where our argument is
00004 P 40E7                   			MOVE	SR,-(SP)		; save interrupts
00006   48E7 80E0              			MOVEM.L	D0/A0-A2,-(SP)		; and some registers
0000A P 007C 0300              			ORI	#DisInt,SR		; interrupts off
0000E                          	;
0000E                          			Dbs	#'transmit routine'
0000E                         1	;;;;;;;;;;;;	PEA	&string
0000E                         1	;;;;;;;;;;;;	Dbgs
0000E                          	;
0000E   4A2D 0000              			TST.B	midi_txst(A5)		; check the flag
00012   661E            00032  			BNE	TxQE			; queue empty - send direct
00014                          			; something here - just add to queue
00014                          			TxEnqueue
00014   302D 0000             1			MOVE	TxByteIn(A5),D0		; where to put it (offset)
00018   45ED 0000             1			LEA	TxQueue(A5),A2		; base of queue
0001C   15AE 000B 0000        1			MOVE.B	Get_Byte(A6),0(A2,D0)	; copy it from the stack to the queue
00022   5240                  1			ADDQ	#1,D0			; bump offset
00024 G 0C40 1000             1			CMP	#TxQSize,D0		; did it wrap?
00028   6602            0002C 1			BNE	@1			; nope
0002A G 4240                  1			MOVE	#0,D0			; you betcha'
0002C   3B40 0000             1	@1		MOVE	D0,TxByteIn(A5)		; replace it
00030   6044            00076  			BRA	TxExit			; go home
00032                          	;
00032                          	TxQE		; must write directly to device (maybe)
00032 G 2078 01D8              			MOVE.L	SCCRd,A0		; read base
00036 G 2278 01DC              			MOVE.L	SCCWr,A1		; write base
0003A   302D 0000              			MOVE	CtlOffset(A5),D0	; ... offset
0003E   0830 0002 0000         			BTST.B	#txBE,0(A0,D0)		; is the device ready
00044   6622            00068  			BNE	FirstByte		; yes - do direct write
00046                          			; device busy - add to queue - interrupt will wake up
00046                          			; handler and send it out when the device is free
00046                          			TxEnqueue
00046   302D 0000             1			MOVE	TxByteIn(A5),D0		; where to put it (offset)
0004A   45ED 0000             1			LEA	TxQueue(A5),A2		; base of queue
0004E   15AE 000B 0000        1			MOVE.B	Get_Byte(A6),0(A2,D0)	; copy it from the stack to the queue
00054   5240                  1			ADDQ	#1,D0			; bump offset
00056 G 0C40 1000             1			CMP	#TxQSize,D0		; did it wrap?
0005A   6602            0005E 1			BNE	@1			; nope
0005C G 4240                  1			MOVE	#0,D0			; you betcha'
0005E   3B40 0000             1	@1		MOVE	D0,TxByteIn(A5)		; replace it
00062 G 422D 0000              			MOVE.B	#midi_ne,midi_txst(A5)	; now it's not empty
00066   600E            00076  			BRA	TxExit			; done
00068                          	;
00068                          	FirstByte	; it really wants this byte...
00068   302D 0000              			MOVE	DataOffset(A5),D0	; ... offset
0006C                          			Delay
0006C   2E97                  1			MOVE.L	(SP),(SP)
0006E   13AE 000B 0000         			MOVE.B	Get_Byte(A6),0(A1,D0)	; copy it from the stack to the device
00074                          			Delay
00074   2E97                  1			MOVE.L	(SP),(SP)
00076                          	;
00076                          	TxExit		; clean up the world
00076   4CDF 0701              			MOVEM.L	(SP)+,D0/A0-A2		; restore registers
0007A P 46DF                   			MOVE	(SP)+,SR		; and interrupts
0007C   4E5E                   			UNLK	A6
0007E   4E75                   			RTS				; in C, the caller cleans up
00080                          			ENDP
                               	;
                               	; midi_rx - get a byte from the port (leave it in D0 as a long)
                               	; We return -1 if nothing is available
                               	;
00000                          	midi_rx		PROC	EXPORT
00000                          			; don't need link 'cause nothing is passed in
00000 P 40E7                   			MOVE	SR,-(SP)		; save the interrupts
00002   48E7 60E0              			MOVEM.L	D1-D2/A0-A2,-(SP)	; and some registers
00006 P 007C 0300              			ORI	#DisInt,SR		; turn off the interrupts
0000A                          	;
0000A                          			Dbs	#'receive routine'
0000A                         1	;;;;;;;;;;;;	PEA	&string
0000A                         1	;;;;;;;;;;;;	Dbgs
0000A                          	;
0000A   4A2D 0000              			TST.B	midi_rxst(A5)		; any data available?
0000E   6708            00018  			BEQ	@1			; yes
00010   303C FFFF              			MOVE.W	#NoData,D0		; no-data flag
00014   48C0                   			EXT.L	D0			; sign-extend it
00016   602A            00042  			BRA	RxExit			; C returns stuff in D0
00018                          	;
00018   342D 0000              	@1		MOVE	RxByteOut(A5),D2	; ... offset
0001C   45ED 0000              			LEA	RxQueue(A5),A2		; and base
00020 G 7000                   			CLR.L	D0			; start off empty
00022   1032 2000              			MOVE.B	0(A2,D2),D0		; get the byte
00026   5242                   			ADDQ	#1,D2			; up the offset
00028 G 0C42 1000              			CMP	#RxQSize,D2		; wrapped around?
0002C   6602            00030  			BNE	@2			; not yet
0002E G 4242                   			MOVE	#0,D2			; yes - reset it to the top
00030                          	;
00030   3B42 0000              	@2		MOVE	D2,RxByteOut(A5)	; and save it
00034   322D 0000              			MOVE	RxByteIn(A5),D1		; now look at the input side
00038   B242                   			CMP	D2,D1			; is the queue now empty?
0003A   6606            00042  			BNE	RxExit			; no - don't worry about it
0003C   1B7C 00FF 0000         			MOVE.B	#midi_em,midi_rxst(A5)	; yes - set the flag
00042                          	;
00042                          	RxExit
00042   4CDF 0706              			MOVEM.L	(SP)+,D1-D2/A0-A2	; restore registers
00046 P 46DF                   			MOVE	(SP)+,SR		; and interrupts
00048   4E75                   			RTS
0004A                          			ENDP
                               	;
                               	; reset the modem port - must call before quitting because the trap
                               	; vectors will be invalid once we exit!!!
                               	;
00000                          	midi_reset	PROC	EXPORT
00000   3B7C 0002 0000         			MOVE	#aCtl,CtlOffset(A5)	; prepare globals
00006   3B7C 0006 0000         			MOVE	#aData,DataOffset(A5)
0000C   1B7C 0080 0000         			MOVE.B	#aReset,ChnReset(A5)
00012   3B7C 0018 0000         			MOVE	#arxIO,RxIntOffset(A5)
00018   3B7C 0010 0000         			MOVE	#atxIO,TxIntOffset(A5)
0001E   3B7C 001C 0000         			MOVE	#aspRC,SpecRecCond(A5)
00024                          	;
00024 P 40E7                   			MOVE	SR,-(SP)		; save interrupts
00026   2F08                   			MOVE.L	A0,-(SP)
00028 P 007C 0300              			ORI	#DisInt,SR		; disable interrupts
0002C                          	;
0002C                          			Dbs	#'reset routine'
0002C                         1	;;;;;;;;;;;;	PEA	&string
0002C                         1	;;;;;;;;;;;;	Dbgs
0002C                          	;
0002C G 2078 01DC              			MOVE.L	SCCWr,A0		; base for writing
00030 G D0ED 0000              			ADD	CtlOffset(A5),A0	; and the offset
00034                          	;
00034                          			Lscc	#SCC9,ChnReset(A5)	; reset channel
00034   103C 0009             1			MOVE.B	#SCC9,D0
00038   1080                  1			MOVE.B	D0,(A0)
0003A                         1			Delay
0003A   2E97                  2			MOVE.L	(SP),(SP)
0003C   102D 0000             1			MOVE.B	ChnReset(A5),D0
00040   1080                  1			MOVE.B	D0,(A0)
00042                         1			Delay
00042   2E97                  2			MOVE.L	(SP),(SP)
00044                          			Lscc	#SCC15,#DCD_int		; Mr. Mousie stays on
00044   103C 000F             1			MOVE.B	#SCC15,D0
00048   1080                  1			MOVE.B	D0,(A0)
0004A                         1			Delay
0004A   2E97                  2			MOVE.L	(SP),(SP)
0004C   103C 0008             1			MOVE.B	#DCD_int,D0
00050   1080                  1			MOVE.B	D0,(A0)
00052                         1			Delay
00052   2E97                  2			MOVE.L	(SP),(SP)
00054                          			Lscc	#SCC0,#RstExtSt		; reset ext status
00054 G 4200                  1			MOVE.B	#SCC0,D0
00056   1080                  1			MOVE.B	D0,(A0)
00058                         1			Delay
00058   2E97                  2			MOVE.L	(SP),(SP)
0005A   103C 0010             1			MOVE.B	#RstExtSt,D0
0005E   1080                  1			MOVE.B	D0,(A0)
00060                         1			Delay
00060   2E97                  2			MOVE.L	(SP),(SP)
00062                          			Lscc	#SCC0,#RstExtSt		; reset ext status
00062 G 4200                  1			MOVE.B	#SCC0,D0
00064   1080                  1			MOVE.B	D0,(A0)
00066                         1			Delay
00066   2E97                  2			MOVE.L	(SP),(SP)
00068   103C 0010             1			MOVE.B	#RstExtSt,D0
0006C   1080                  1			MOVE.B	D0,(A0)
0006E                         1			Delay
0006E   2E97                  2			MOVE.L	(SP),(SP)
00070                          			Lscc	#SCC1,#EnMoInt		; enable mouse interrupts
00070   103C 0001             1			MOVE.B	#SCC1,D0
00074   1080                  1			MOVE.B	D0,(A0)
00076                         1			Delay
00076   2E97                  2			MOVE.L	(SP),(SP)
00078   103C 0001             1			MOVE.B	#EnMoInt,D0
0007C   1080                  1			MOVE.B	D0,(A0)
0007E                         1			Delay
0007E   2E97                  2			MOVE.L	(SP),(SP)
00080                          			Lscc	#SCC9,#MIntEn		; master interrupt
00080   103C 0009             1			MOVE.B	#SCC9,D0
00084   1080                  1			MOVE.B	D0,(A0)
00086                         1			Delay
00086   2E97                  2			MOVE.L	(SP),(SP)
00088   103C 000A             1			MOVE.B	#MIntEn,D0
0008C   1080                  1			MOVE.B	D0,(A0)
0008E                         1			Delay
0008E   2E97                  2			MOVE.L	(SP),(SP)
00090                          	;
00090                          	; We don't turn the vectors back to where they were...
00090                          	; We also don't leave interrupts enabled so it's OK
00090                          	;
00090 G 205F                   			MOVE.L	(SP)+,A0		; restore register
00092 P 46DF                   			MOVE	(SP)+,SR		; interrupts on
00094   4E75                   			RTS
00096                          			ENDP
                               	;
                               			END

Elapsed time: 9.00 seconds.

Assembly complete - no errors found.  1662 lines.
//E*O*F mdr.lst//

echo x - midi.h
cat > "midi.h" << '//E*O*F midi.h//'
/* 
 * midi definitions and declarations
 *
 * Steven A. Falco  3/23/87
 *
 */
 
#define MIDI_NE		0	/* queue has 1 or more bytes available */
#define MIDI_EM		(-1)	/* queue is empty */

extern void midi_init();	/* set up hardware & software */
extern void midi_reset();	/* unset - very important to avoid crashes */
extern void midi_tx();		/* send a byte (but declare arg as a long) */
extern long midi_rx();		/* get a byte (returns -1 on "nothing available" */
extern char midi_txst;		/* transmit queue state (flags above) */
extern char midi_rxst;		/* receive queue state */
//E*O*F midi.h//

echo x - t2.c
cat > "t2.c" << '//E*O*F t2.c//'
/* example use of midi driver for mac */

/* S. A. Falco   3/24/87 */

#include <stdio.h>
#include "midi.h"

main()
{
	int i,j,k,l;
	char buf[512];
	
	setbuf(stdout, NULL);

	midi_init();
	
	for(k = 0; k < 4; k++) {		/* loop playing notes */
		for(i = 0; i < 36; i++) {	
			l = rand() & 0x3f;	/* pick a note */

			midi_tx(0x90);		/* key down */
			midi_tx(l + 40);	/* the pitch (must offset from 0) */
			midi_tx(0x50);		/* how hard to hit it */

			midi_tx(0x90);
			midi_tx(l + 47);	/* also play a fifth above it */
			midi_tx(0x40);

			midi_tx(0x90);
			midi_tx(l + 43);	/* and a minor third */
			midi_tx(0x45);

			for(j = 0; j < (1 << 14); j++) /* play for a fraction of a second */
				;

			midi_tx(0x80);		/* key up */
			midi_tx(l + 40);
			midi_tx(0x40);

			midi_tx(0x80);
			midi_tx(l + 47);
			midi_tx(0x40);

			midi_tx(0x80);
			midi_tx(l + 43);
			midi_tx(0x40);
		}
	}

	
	while(midi_txst == MIDI_NE) /* give it time to be sent */
		;
	for(k = 0; k < (1 << 14); k++) /* plus a bit for the hardware fifo */
		;
		
	printf("ready to enter receive loop\n");
	j = 0;
	while(j < 100) {
		k = midi_rx();		/* try to read the midi channel */
		if(k != -1) {		/* -1 means nothing in the queue */
			printf("%x ", k); /* print it */
			if(++j % 3 == 0) {
				printf("\n"); /* group them in threes */
			}
		}
	}
	
	midi_reset();	/* CRITICAL - if the interrupts are not reset, you'll
			 * crash when we exit!
			 */
	exit(0);
}
//E*O*F t2.c//

exit 0
