
;************************************************************************
;*	TITLE:			PARSER.S				*
;*	PROJECT:		JAGUAR					*
;*	FUNCTION:		MIDI REALTIME PARSER			*
;*	PROGRAMMER:		Hans-Martin Krober			* 
;*	DATE:			3/95					*
;*									*
;*									*
;*            COPYRIGHT 1993,1994,1995 Atari U.S. Corporation           *
;*          UNATHORIZED REPRODUCTION, ADAPTATION, DISTRIBUTION,         *
;*          PERFORMANCE OR DISPLAY OF THIS COMPUTER PROGRAM OR          *
;*        THE ASSOCIATED AUDIOVISUAL WORK IS STRICTLY PROHIBITED.       *
;*                            ALL RIGHTS RESERVED.                      *
;*									*
;************************************************************************

;*****************************************************************************
;*	GLOBALS
;*****************************************************************************

		.globl		_parser
		.globl		_init_parse
		.globl		event
		.globl		_event
		.globl		_channel
		.globl		priority	
						
;*****************************************************************************
;*	EXTERNALS
;*****************************************************************************

		.extern		_midindx	; index into midi buffer	
		.extern		_midibuf	; midi buffer
	
		.extern		freq_tab

		.extern		_gl_pitchbend
		.extern		_patch_trans
		.extern		_alloc_voice
		.extern		_free_voice
		.extern		_find_voices
		.extern		_find_patches
		.extern		_init_voices
		.extern		__effect_mode
		.extern		_program_change
		.extern		_play_midi
		.extern		_c_play_midi
		.extern		play_flag
		.extern		midi_score

		.extern		_midi_filter

		.extern		MainScreen

		.extern		_dbg_envmem
		.extern		__dbuf2p
;*****************************************************************************
;*	DEFINES
;*****************************************************************************

MIDIBUFSIZE	equ		8192		; size of midi buffer
EVNT_NOTE_ON	equ		$80000000	; Code for Note On
EVNT_NOTE_OFF	equ		$00000000	; Code for Note Off
EVNT_CTRL	equ		$40000000	; Code for Pitch Bend

CTRL_PITCH	equ		$00000000
CTRL_PAN	equ		$00200000
CTRL_VOLUME	equ		$00400000
CTRL_SURROUND	equ		$00600000

FILTER_PRGCHANGE equ		0
FILTER_PAN	 equ		1
FILTER_VOLUME 	 equ		2
FILTER_PITCHBEND equ		3

;*****************************************************************************
;	INIT MIDI real time parser
;*****************************************************************************

_init_parse:
	move.w	#0, rdmidindx
	move.w	#0, cmd
	move.w	#0, channel
	move.w	#0, patch
	move.w	#0, ndata

	move.w	#0,xyz
	
	jsr	_init_voices

	rts

;*****************************************************************************
;	MIDI real time parser
;*****************************************************************************

#define RDMIDIX		d1
#define MIDIBYTE	d2
#define TEMP1		d3
#define TEMP2		d4
#define NDATA		d5
#define LOOPCOUNT	d7
#define MIDIBUF		a0
#define DATABUF		a1
#define NPARAMTAB	a2
#define MIDIFUNCTAB	a3
#define FPTR		a4

_parser:
	movem.l	d1-d7/a0-a6,-(a7)	; store used registers

jsr _dbg_envmem


	move.w	play_flag,TEMP1		; first, check to see if we have
	tst.w	TEMP1			; to play anything
	beq	.la
	jsr	_play_midi
.la:
	move.w	rdmidindx,RDMIDIX	; read index

	move.w	play_flag,TEMP1	
	tst.w	TEMP1			; are we allowed to parse
	bne	.end			; anything again

;***	Now comes the parsing
;***	first, let's check if there is anything in the buffer

	move.l	_midindx, LOOPCOUNT
	sub.w	RDMIDIX, LOOPCOUNT	; loop count (d7)
	bpl	.l0
	add.w	#MIDIBUFSIZE,LOOPCOUNT	; if < 0 then +8000 = difference
	add.w	#1, xyz
.l0:
	cmp.w	#0, LOOPCOUNT
	beq	.end

	subq.w	#1, LOOPCOUNT
	
	move.l	#_midibuf, MIDIBUF	; midi buffer


;***	Loop Setup
	move.l	#dataBuf,DATABUF	; Dat Buffer in a1
	move.l	#nParamTab,NPARAMTAB	; Table telling us how many paramters
	move.l	#midiFuncTab,MIDIFUNCTAB; MIDI Function table	
	clr.l	MIDIBYTE
.loop:
;**************************
;*	parse the midi byte

	move.b	(MIDIBUF,RDMIDIX),MIDIBYTE	; byte from midi buffer

	addq.w	#1, RDMIDIX
	cmp.w	#MIDIBUFSIZE, RDMIDIX
	blt	.l1
	move.w	#0, RDMIDIX
.l1:
	cmp.b	#$f7, MIDIBYTE		; skip > $f7 events
	bhi	.loopend

	btst	#7, MIDIBYTE		; check for new command
	beq	.l2			; no, go on

;***	Command Byte
	move.w	MIDIBYTE, TEMP1		; yes -> save command and MIDI channel
	and.w	#$0f, TEMP1
	move.w	TEMP1, channel		; MIDI channel
	and.w	#$f0, MIDIBYTE
	move.w	MIDIBYTE, cmd		; MIDI command
	move.w	#0, ndata		; number of data bytes
	bra	.loopend			

;***	Data Byte
.l2:	
	move.w	ndata,NDATA
	move.b	MIDIBYTE,(DATABUF,NDATA); store data byte in data buffer
	addq.w	#1,NDATA
	move.w	NDATA,ndata

	clr.l	TEMP1
	move.w	cmd,TEMP1		; get command
	cmp.w	#$f0,TEMP1		; system msg?
	bhs	.sysmsg

.l3:
	lsr.w	#4,TEMP1		; move high nibble down
	subq.w	#8,TEMP1		; for use as and index
	lsl.w	#1, TEMP1		; word addressing

	move.w	(NPARAMTAB,TEMP1),TEMP2	; number now in d4
	
	cmp.w	TEMP2,NDATA		; check if we have all parameters yet
	bne	.loopend		; no, go on

	lsl.w	#1,TEMP1		; long adressing
	move.l	(MIDIFUNCTAB,TEMP1),FPTR; yes, then get the function which
					; handles this event
	jsr	(FPTR)			; go for it
	clr.w	ndata
	bra	.end

.sysmsg:
	cmp.w	#7, channel		; channel is lower nibble of sys cmd
	beq	.sysex			; check for for sys ex end

	clr.l	TEMP2
	move.w	channel,TEMP2
	lsl.w	#4,TEMP2
	add.w	TEMP2,TEMP1		; ok we have a system msg and it's
	bra	.l3			; not a eox ($f7), so we just add
					; the lower nibble (which is in 
					; channel) to d3 (cmd) which will be
					; used as an index for the tables
					; and go back to the code handling
					; midi events


;*	end of sys ex
.sysex:
	pea	dataBuf			; data buffer
	move.w	NDATA, -(sp)		; # of paramters
	jsr	systemEx		; go for it
	addq.l	#6, sp
	clr.w	ndata

.loopend:
	dbra	LOOPCOUNT, .loop
	
.end:
	move.w	RDMIDIX, rdmidindx
	movem.l	(a7)+, d1-d7/a0-a6
	rts

;****************************************************************************
;*	MIDI Note On 
;*****************************************************************************
#define NOTE		d4
#define PATCH		d5
#define VELOCITY	d6
#define EVENT		d1
#define VOICE		d2
#define SHIFT		d3


note_on:
	movem.l	d1-d7/a0-a6,-(a7)	; store used registers
	clr.l	NOTE
	clr.l	VELOCITY
	move.l	#dataBuf, DATABUF	; here's the data
	move.b	(DATABUF)+, NOTE	; get note number
	move.b	(DATABUF)+, VELOCITY	; get velocity

	cmp.b	#0, VELOCITY
	bne	.nn1
	jsr	note_off		; note on with 0 velocity is a note off
	bra	.nn_end

;************************************
;*	let's find a voice to be used
.nn1:
	pea	priority
	move.w	NOTE,-(sp)		; do patch translation
	move.w	channel, patch
	pea	patch
	jsr	_patch_trans
	add.l	#10,sp
	move.w	d0,NOTE
	clr.l	PATCH
	move.w	patch,PATCH
		
	clr.l	EVENT			; create synth event
	lsl.l	#2, NOTE
	move.l	#freq_tab, a0
	add.l	NOTE, a0
	move.l	(a0), NOTE

	move.l	#24,SHIFT
	lsl.l	SHIFT,VELOCITY
	or.l	VELOCITY,EVENT
	
	lsl.l	#7, NOTE
	or.l	NOTE,EVENT	
	or.l	PATCH,EVENT		; finally, the voice
	or.l	#EVNT_NOTE_ON,EVENT	; last but not least, the event code

	move.l	EVENT, event
	move.w	#1, play_flag
;* Debug Code
.non_1:
	move.w	#$ffff,MainScreen
	move.w	#$ffff,MainScreen+2

.nn_end:
	movem.l	(a7)+, d1-d7/a0-a6
	rts

;****************************************************************************
;*	MIDI Note Off
;*****************************************************************************

#define NOTE		d4
#define PATCH		d5
#define EVENT		d1
#define VOICE		d6
#define SHIFT		d3

note_off:
	movem.l	d1-d7/a0-a6,-(a7)	; store used registers
	clr.l	NOTE
	move.l	#dataBuf, DATABUF	; here's the data
	move.b	(DATABUF)+, NOTE	; get note number

;************************************
;*	let's find the voice which is used
	pea	priority
	move.w	NOTE,-(sp)		; do patch translation
	move.w	channel, patch
	pea	patch
	jsr	_patch_trans
	add.l	#10,sp
	move.w	d0,NOTE

	clr.l	PATCH
	clr.l	EVENT
	move.w	patch,PATCH

	lsl.l	#2, NOTE
	move.l	#freq_tab, a0
	add.l	NOTE, a0
	move.l	(a0), NOTE
	lsl.l	#7, NOTE
	or.l	NOTE,EVENT	
	or.l	PATCH,EVENT		; finally, the patch		
					; event code is 0, so we don't have
					; to do anything
	move.l	EVENT, event
	move.w	#1, play_flag

;* Debug Code
.noff_1:
	move.w	#$0,MainScreen
	move.w	#$0,MainScreen+2

	movem.l	(a7)+, d1-d7/a0-a6
	rts

;;

paftertouch:
	rts

#define CHANNEL		d5
#define EVENT		d1
#define PATCH		d6
#define VALUE		d3
#define CTRL		d4
#define SHIFT		d7

ctrlchange:

	movem.l	d0-d7/a0-a6,-(a7)	; store used registers
	clr.l	CTRL
	clr.l	VALUE
	clr.l	PATCH
	clr.l	EVENT
	move.l	#22, SHIFT
			; create synth event
	move.l	#dataBuf, DATABUF	; here's the data
	move.b	(DATABUF)+, CTRL	; get controller #
	move.b	(DATABUF),  VALUE	; get value
	move.w	channel, CHANNEL
	cmp.w	#7, CTRL		; volume controller
	bne	ccpan
	move.l	_midi_filter, d0
	btst	#FILTER_VOLUME, d0
	bne	ccx
	move.l	#CTRL_VOLUME, CTRL
	bra	cc1
ccpan:
	cmp.w	#10, CTRL		; pan controller
	bne	ccsurround
	btst	#FILTER_PAN, d0
	bne	ccx

	move.l	#CTRL_PAN, CTRL
	bra 	cc1

ccsurround:
	cmp.w	#9, CTRL		; surround controller
	bne	ccx
	move.l	__effect_mode, a0
	move.l	(a0), d0
	and.l	#$ffff,d0
	cmp	#2, d0
	bne	ccx
	move.l	#CTRL_SURROUND, CTRL

	move.l	VALUE, EVENT
	lsl.l	#7, EVENT
	or.l	PATCH, EVENT	
	or.l	CTRL, EVENT
	or.l	#EVNT_CTRL, EVENT
	move.l	EVENT, event
	move.w	#1, play_flag
	jsr	_c_play_midi
	bra	ccx	
cc1:
	move.w	CHANNEL, -(a7)
	jsr	_find_patches
	addq.w	#2, a7
	cmp.w	#$ffff, d0
	beq	ccx

	move.w	d0, PATCH
	move.l	VALUE, EVENT
	lsl.l	#7, EVENT
	or.l	PATCH, EVENT	
	or.l	CTRL, EVENT
	or.l	#EVNT_CTRL, EVENT
cc2:
	cmp.w	#1, play_flag
	bne	cc3
	jsr	_c_play_midi
	bra	cc2
cc3:
	move.l	EVENT, event
	move.w	#1, play_flag
	jsr	_c_play_midi
	move.w	#$ffff, CHANNEL
	bra	cc1	

ccx:
	movem.l	(a7)+, d0-d7/a0-a6
	rts


#define CHANNEL		d5
#define PATCH		d1

prgchange:
;****************************************
rts	;********************************
;****************************************
	movem.l	d0-d7/a0-a6,-(a7)	; store used registers
	move.l	_midi_filter, d0
	btst	#FILTER_PRGCHANGE, d0
	bne	pcx

	clr.l	PATCH
	clr.l	CHANNEL
	move.l	#dataBuf, DATABUF	; here's the data
	move.b	(DATABUF), PATCH	; get patch
	move.w	channel, CHANNEL

	move.w	PATCH, -(a7)
	move.w	CHANNEL, -(a7)
	jsr	_program_change
	addq.w	#4, a7
pcx:
	movem.l	(a7)+, d0-d7/a0-a6
	rts

aftertouch:
	rts

#define CHANNEL		d5
#define EVENT		d1
#define PATCH		d6
#define PB1		d3
#define PB2		d4

pitchbend:
	movem.l	d0-d7/a0-a6,-(a7)	; store used registers
	move.l	_midi_filter, d0
	btst	#FILTER_PITCHBEND, d0
	bne	pb1

	clr.l	PB1
	clr.l	PB2
	clr.l	CHANNEL
	clr.l	PATCH
	clr.l	EVENT			; create synth event
	move.l	#dataBuf, DATABUF	; here's the data
	move.b	1(DATABUF), PB1		; get pitchbend MSB
	lsl.l	#7, PB1
	move.b	0(DATABUF), PB2
	or.l	PB2, PB1		; get pitchbend LSB
	lsr.l	#1, PB1

	cmp.w	#2, _gl_pitchbend
	bne	pb0a

	cmp.w	#$1000, PB1		; this is for only +/- 2 semitones
	blt	pb0
	lsr.l	#4, PB1
	bset	#12, PB1
	bra	pb0a
pb0:
	lsr.l	#3, PB1
	add.l	#$C00, PB1
	addq.l	#1, PB1			; round up	
pb0a:

	move.w	channel, CHANNEL

pb1:
	clr.l	EVENT
	move.w	CHANNEL, -(a7)
	jsr	_find_patches
	addq.w	#2, a7

	cmp.w	#$ffff, d0
	beq	pbx


	move.w	d0, PATCH

	move.l	PB1, EVENT
	lsl.l	#7, EVENT
	or.w	PATCH, EVENT
	or.l	#CTRL_PITCH, EVENT
	or.l	#EVNT_CTRL, EVENT

pb2:
	cmp.w	#1, play_flag
	bne	pb3
	jsr	_c_play_midi
	bra	pb2
pb3:
	move.l	EVENT, event

	move.w	#1, play_flag
	jsr	_c_play_midi
	move.w	#$ffff, CHANNEL
	bra	pb1	

pbx:
	movem.l	(a7)+, d0-d7/a0-a6
	rts

systemEx:
	rts

sys_ignore:
	rts




;*****************************************************************************

	.data

;***
;	Table which contains how many paramters each MIDI function requires
;
nParamTab:
	.dc.w	2			; Note off ($80)
	.dc.w	2			; Note on ($90)
	.dc.w	2			; Plyphonic aftertouch ($A0)
	.dc.w	2			; Control Change ($B0)
	.dc.w	1			; Patch change ($C0)
	.dc.w	1			; Aftertouch ($D0)
	.dc.w	2			; Pitch bend ($E0)
	.dc.w	$ffff			; Sys Ex($f0)
	.dc.w	1			; Time Code Quarte Frame ($f1)
	.dc.w	2			; Song pos ptr ($f2)
	.dc.w	1			; Song select ($f3)
	.dc.w	0			; Undefinded ($f4)
	.dc.w	0			; Undefinded ($f5)
	.dc.w	0			; Tune Request ($f6)
	.dc.w	0			; Eox ($f7)

;***
;	Table of functions for handling MIDI events
midiFuncTab:
	.dc.l	note_off 		; Note off ($80)
	.dc.l	note_on 		; Note on ($90)
	.dc.l	paftertouch 		; Polyphonic aftertouch ($a0)
	.dc.l	ctrlchange 		; Controller change ($b0)
	.dc.l	prgchange		; Program change ($c0)
	.dc.l	aftertouch 		; Channel pressure (aftertouch) ($d0)
	.dc.l	pitchbend 		; Pitch bend ($e0)
	.dc.l	systemEx 		; System Exclusive ($f0)
	.dc.l	sys_ignore		; Time Code Quarter Fram ($f1)
	.dc.l	sys_ignore		; Song pos ptr ($f2)
	.dc.l	sys_ignore		; Song select ($f3)
	.dc.l	sys_ignore		; undefined ($f4)
	.dc.l	sys_ignore		; undefined ($f5)
	.dc.l	sys_ignore		; Tune request ($f6)
	.dc.l	systemEx		; eox ($f7)
		

;*****************************************************************************

	.bss
_rdmidindx::
rdmidindx:
	.ds.w	1			; Read index for MIDI buffer
cmd:
	.ds.w	1			; MIDI command
_channel:
channel:	
	.ds.w	1			; MIDI channel

patch:	
	.ds.w	1			; synth patch
priority:	
	.ds.w	1			; synth priority

ndata:
	.ds.w	1			; # of data bytes

dataBuf:
	.ds.b	256			; MIDI Paramter Buffer

midiOutBuf:
	.ds.b	256			; MIDI Output Buffer to Synth	

event:					; Synth event
_event:
	.ds.l	1
xyz:
	.ds.w	1
