;	Routines for sending/receiving MIDI data to/from the serial port
;	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
;	Written by Teijo Kinnunen.

;	For a complete description of these routines, see the
;	accompanying text file.

;	Please feel free to use these routines in your own programs!!

;	If you want to install the input handler, set INPUT to 1.
INPUT	EQU	0

;	These XDEFs allow you to link e.g. with C-programs.

	XDEF	_GetSerial
	XDEF	_FreeSerial
	XDEF	_AddMIDIData
	XDEF	_ResetMIDI

	CODE	;some assemblers require SECTION

_GetSerial:	move.l	a6,-(sp)
		bsr.s	GetSer
		tst.l	d0
		beq.s	GSerial_exit
		movea.l	4,a6
		jsr	-$84(a6)	;Forbid()
		lea	$15e(a6),a0
		lea	serdevname(pc),a1
		jsr	-$114(a6)	;FindName()
		tst.l	d0
		beq.s	GSerial_nosd
		move.l	d0,a1
		jsr	-$1b6(a6)	;RemDevice()
GSerial_nosd	jsr	-$8a(a6)	;Permit()
		bsr.s	GetSer
GSerial_exit	move.l	(sp)+,a6
		rts

GetSer:		move.l	a6,-(sp)
		movea.l	4,a6
		moveq	#0,d0
		lea	miscresname(pc),a1
		jsr	-$1f2(a6)
		tst.l	d0
		beq.s	GS_error
		move.l	d0,miscresbase
		move.l	d0,a6
		moveq	#0,d0
		lea	myname(pc),a1
		jsr	-$6(a6)	;AllocMiscResource()
		tst.l	d0
		bne.s	GS_error
		st	serportalloc
;It's usually required to call _InitSerial only once, so it's
;convenient to do it now.
		bsr.s	_InitSerial
		moveq	#0,d0
GS_exit		move.l	(sp)+,a6
		rts
GS_error	moveq	#-1,d0
		bra.s	GS_exit

_InitSerial	move.l	a6,-(sp)
		move.b	serportalloc(pc),d0
		beq.s	InitSer_exit
		move.w	#114,$dff032	;$dff032 = SERPER
		moveq	#0,d0
		lea	tbeinterrupt(pc),a1
		movea.l	4,a6
		jsr	-$a2(a6)	;SetIntVector()
		move.l	d0,prevtbe
	IFNE INPUT
		moveq	#11,d0
		lea	rbfinterrupt(pc),a1
		jsr	-$a2(a6)	;SetIntVector()
		move.l	d0,prevrbf
		move.w	#$8801,$dff09a
	ELSEIF
		move.w	#$8001,$dff09a
	ENDC
InitSer_exit	move.l	(sp)+,a6
		rts

SerIntHandler:	move.w	#$4000,$9a(a0)	;$dff09a = INTENA
		addq.b	#1,$126(a6)	;ExecBase->IDNestCnt
		move.w	#1,$9c(a0)	;$dff09c = INTREQ
		move.b	bytesinbuff(pc),d0
		beq.s	SerInt_bufempty
		movea.l	4(a1),a5
		move.w	#$100,d1
		move.b	(a5)+,d1
		move.w	d1,$30(a0)	;$dff030 = SERDAT
		cmpa.l	a1,a5
		bne.s	SerInt_resrp
		lea	sendbuffer(pc),a5
SerInt_resrp	subq.b	#1,d0
		move.b	d0,8(a1)
		move.l	a5,4(a1)
SerInt_exit	subq.b	#1,$126(a6)
		bge.s	SerInt_X
		move.w	#$c000,$9a(a0)
SerInt_X	rts
SerInt_bufempty	st	9(a1)
		bra.s	SerInt_exit

_AddMIDIData:	move.b	serportalloc(pc),d1
		beq.s	AMD_rts
		movem.l	a2/a6,-(sp)
		movea.l	4,a6
		move.w	#$4000,$dff09a
		addq.b	#1,$126(a6)
		lea	buffptr(pc),a2
		tst.b	9(a2)
		beq.s	AMD_noTBEreq
		clr.b	9(a2)
		move.w	#$8001,$dff09c
AMD_noTBEreq	movea.l	(a2),a1
AMD_dataloop	move.b	(a0)+,d1
		bpl.s	AMD_nostatus
		cmp.b	#$ef,d1
		bhi.s	AMD_nostatus
		cmp.b	lastcmdbyte(pc),d1
		beq.s	AMD_noinsbyte
		move.b	d1,10(a2)
AMD_nostatus	move.b	d1,(a1)+
		addq.b	#1,8(a2)
AMD_noinsbyte	cmpa.l	a2,a1
		bne.s	AMD_noptrreset
		lea	sendbuffer(pc),a1
AMD_noptrreset	subq.b	#1,d0
		bne.s	AMD_dataloop
		move.l	a1,(a2)
		subq.b	#1,$126(a6)
		bge.s	AMD_x
		move.w	#$c000,$dff09a
AMD_x		movem.l	(sp)+,a2/a6
AMD_rts		rts

_ResetMIDI:	move.l	a6,-(sp)
		movea.l	4,a6
		move.w	#$4000,$dff09a
		addq.b	#1,$126(a6)
		lea	buffptr(pc),a1
		lea	sendbuffer(pc),a0
		move.l	a0,(a1)+
		move.l	a0,(a1)+
		clr.b	(a1)+
		addq.l	#1,a1
		clr.b	(a1)
		subq.b	#1,$126(a6)
		bge.s	ResetM_x
		move.w	#$c000,$dff09a
ResetM_x	move.l	(sp)+,a6
		rts

	IFNE INPUT
		XREF	_maintsk	;the task to be signalled
		XREF	_sigmask	;the signal mask
		XREF	_inputbuff	;a 3-byte input note buffer

RBFIntHandler:	move.w	$18(a0),d0	;$dff018 = SERDATR
		move.w	#$0800,$9c(a0)
		tst.b	d0
		bpl.s	RBF_nostatus
		cmp.b	#$f7,d0
		bhi.s	RBF_exit
		move.b	d0,(a1)
		clr.b	3(a1)
RBF_exit	rts
RBF_nostatus	moveq	#0,d1
		move.b	3(a1),d1
		move.b	d0,1(a1,d1.w)
		addq.b	#1,d1
		cmp.b	#2,d1
		bge.s	RBF_signal
		move.b	d1,3(a1)
		rts
RBF_signal	clr.b	3(a1)
		move.b	(a1),d0
		and.b	#$f0,d0
		cmp.b	#$90,d0
		beq.s	RBF_sig
		cmp.b	#$80,d0
		bne.s	RBF_nosig
RBF_sig		lea	_inputbuff,a0
		move.b	(a1)+,(a0)+
		move.b	(a1)+,(a0)+
		move.b	(a1),(a0)
		movea.l	_maintsk,a1
		move.l	_sigmask,d0
		jsr	-$144(a6)	;Signal()
RBF_nosig	rts
	ENDC

_FreeSerial:	move.l	a6,-(sp)
		tst.b	serportalloc
		beq.s	FreeSer_xit
		move.w	#$0801,$dff09a
		movea.l	4,a6
		moveq	#0,d0
		move.l	prevtbe(pc),a1
		jsr	-$a2(a6)	;SetIntVector()
	IFNE INPUT
		moveq	#11,d0
		move.l	prevrbf(pc),a1
		jsr	-$a2(a6)	;SetIntVector()
	ENDC
		movea.l	miscresbase(pc),a6
		moveq	#0,d0
		jsr	-$c(a6)		;FreeMiscResource()
		clr.b	serportalloc
FreeSer_xit	move.l	(sp)+,a6
		rts

;DON'T CHANGE THE ORDER OF THESE 6 ENTRIES
sendbuffer	ds.b	128
buffptr		dc.l	sendbuffer	;buffer WRITE pointer
readbuffptr	dc.l	sendbuffer	;buffer READ pointer
bytesinbuff	dc.b	0
bufferempty	dc.b	-1
lastcmdbyte	dc.b	0

miscresname	dc.b	'misc.resource',0
serportalloc	dc.b	0
;USE THE NAME OF YOUR OWN PROGRAM INSTEAD OF 'MyProggie'
myname		dc.b	'MyProggie',0
tbename		dc.b	'MyProggie serial interrupt',0
serdevname	dc.b	'serial.device',0
		even
miscresbase	dc.l	0
prevtbe		dc.l	0
tbeinterrupt	dc.w	0,0,0,0,0
		dc.l	tbename,buffptr,SerIntHandler

	IFNE INPUT
recmidi		dc.b	0,0,0,0
prevrbf		dc.l	0
rbfinterrupt	dc.w	0,0,0,0,0
		dc.l	rbfname,recmidi,RBFIntHandler
rbfname		dc.b	'MyProggie RBF-interrupt',0
	ENDC

	END
