;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;9090 firmware
;
;
;Copyright Trevor Page 2001,2002
;
;http://introspectiv.org/
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; D E V E L O P M E N T   H I S T O R Y 
;
; This is VERSION 1.2
;
; November 2002: Problems with velocity 
; problems have been solved. 
;
; December: stupid crappy running status prob solved. 
;
;
;
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


;Code contains a lot of crappy comments I left for myself, which 
;are probably better off just being ignored. The code has now
;been well-tested and this is the final version of it. 



;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;			Port Usage:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; RB0/INT: MIDI input  
; RB1 to RB6 - data lines
; RB7: sync output - to Q87
; RA0: DAC clock	
; RA1: first 40174 (U43) latch	
; RA2: second 40174 (U41) latch
; RA3: enable line for BOTH 4051's
; RA4: MIDI LED (sink; 0V = LED on)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;    Mapping of PortB to latches and R-2R array
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;	4174(0)	4174(1)		DAC-R2R/BIT
;RB1	ride	hh		10K	bit4
;RB2	hhsel	?midtom		40K	bit2
;RB3	sync	?lowtom		160K	bit0
;RB4	hitom	?snaredrum	80K	bit1
;RB5	h'clap	?bassdrum	20K	bit3
;RB6	crash	r'shot		5K	bit5
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;



#define trig_rimshot	bsf triggers1,6	;	
#define trig_handclap	bsf triggers0,5	;

#define trig_bassdrum	bsf triggers1,3	;
#define trig_snaredrum	bsf triggers1,4	;
#define trig_lowtom	bsf triggers1,5	;
#define trig_midtom	bsf triggers1,2	;

#define trig_hightom	bsf triggers0,4	;
#define trig_hihat	bsf triggers1,1	;
#define trig_crash	bsf triggers0,6	;
#define trig_ride	bsf triggers0,1	;
#define sel_openhat	bcf flags,hhsel ;;;nop		;  :-)
#define sel_closedhat	bsf flags,hhsel ;;;;bsf triggers0,2	;

OPT_reg equ h'01'		;Option register (BANK 1)
RBPU	equ 7			;Port B weak pull-ups enable: 0 = enable.
INTEDG	equ 6			;Interrupt edge selection bit: 0 = interrupt on falling edge on RB0/INT pin.
T0CS	equ 5			;TMR0 clock source selection: 0 = internal instruction cycle clock , 1 = RA4
T0SE	equ 4			;TMR0 source edge select bit
PSA	equ 3 			;Prescaler assignment : 1 = WDT, 0 = TMR0
PS2	equ 2			;Prescaler rate selection bits
PS1	equ 1			;	"	"
PS0	equ 0			;	"	"
				;
INTCON	equ h'0B'		;Interrupt Control Register
GIE	equ 7			;Global Interrupt Enable: Set to enable all un-masked interrupts
EEIE	equ 6			;EE write complete timer interrupt enable : 	1 = enable
T0IE	equ 5			;Timer 0 overflow interrupt enable :		1 = enable 
INTE	equ 4			;External interrupt RB0/INT pin enable :	1 = enable 
RBIE	equ 3			;Port B pin change interrupts (RB7:RB4) enable :1 = enable
T0IF	equ 2			;Timer 0 overflow interrupt flag	:must be cleared in software
INTF	equ 1			;External interrupt RB0/INT flag	:must be cleared in software???
RBIF	equ 0			;Port B pin change interrupt flag	:must be cleared in software



key_bassdrum:	equ h'48'
key_snaredrum:	equ h'49'
key_lowtom:	equ h'50'
key_midtom:	equ h'51'
key_hightom:	equ h'40'
key_handclap:	equ h'3e'
key_rimshot:	equ h'3c'
key_crash:	equ h'41'
key_ride:	equ h'43'
key_openhat:	equ h'45'
key_closedhat:	equ h'47'



;40174 inputs / outputs: pins  6 to 7, 11 to 10, 13 to 12, 4 to 5, 3 to 2, 14 to 15. 


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;Multiplexer settings for setting
;velocity of each drum circuit
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;		mpx0	mpx1
;Rimshot 	y7	y4(n/c)
;handclap	y4	y4(n/c)
;bassdrum	y2(n/c)	y3?
;snaredrum	y2(n/c)	y0?
;lowtom		y2(n/c)	y1?
;midtom		y2(n/c)	y2?
;hightom	y0	y4(n/c)
;hihat		y2(n/c)	y5
;crash		y2(n/c)	y6
;ride		y2(n/c)	y7
;
;These are defined in macro definitions that follow
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;



;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;	Mapping of PortB to MPX address lines
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;addr#		mpx0	mpx1
;a0		rb6	rb1
;a1		rb5	rb2
;a2		rb4	rb3
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;	EEPROM MEMORY USAGE
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;00h to 14h	:	21 locations for mapping data
;15h		;	Sustain
;16h		;	CHECKSUM of locations 00h:15h
;17h		;	Complimented checksum of 00h:15h
;18h		;	Channel
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;



	list p=16f84 r=dec

	__config 0x3ff1		;	3ff1 = not code protected

mpx0a0:	equ 6 
mpx0a1:	equ 5
mpx0a2:	equ 4
mpx1a0:	equ 1
mpx1a1:	equ 2
mpx1a2:	equ 3

dac_en:	equ 0
latch0:	equ 1			;DATA LOADED ON LOW-TO-HIGH TRANSITION
latch1:	equ 2
mpx_en:	equ 3
led:	equ 4



#define PAGE0 bcf STATUS,5
#define PAGE1 bsf STATUS,5

PORTB:  equ h'06'		;Port B data register
PORTA:  equ h'05'		;Port A data register
TRISB:  equ h'06'		;Port B Data Direction Register [DDR]
TRISA:  equ h'05'		;Port A Data Direction Register [DDR]
STATUS: equ h'03'		;Microcontroller Status Register byte
C:      equ h'0'		;Carry flag bit
W:      equ h'0'		;Working Register
F:      equ h'1'		;File
Z:      equ h'2'		;Zero flag bit

TMR0:	equ h'01'
OPTIONREG:	equ h'01'
INTCON:	equ h'0B'    
PCL     equ h'02'		;program counter 


INDF	equ h'00'
FSR	equ h'04'

EEDATA	equ 0x08		;
EEADR	equ 0x09		;
EECON1	equ 0x88		;
EECON2	equ 0x89		;
EEIF	equ 4
WREN	equ 2
RD	equ 0
WR	equ 1



;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;		USER GPR DEFINITIONS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

BYTE:   equ 0x0c		;Temporary MIDI data storage


TEMP1:  equ 0x0e      		;temp
TEMP0: 	equ 0x0f		;temp used inside interrupt service routine

triggers0: equ h'10'		;triggers 0 to 5   xttttttx
triggers1: equ h'11'		;triggers 6 to 11  xttttttx 	


VELOCITY: 	equ h'14'	;
PITCH:		equ h'15'	;
statusbyte:	equ h'16'
currentbytenum:	equ h'17'

flags:		equ h'18'	; various special flags and associated bits
sync_clock:	equ 1		; state of sync clock output (probably won't be used)
sync_runstop:	equ 2		; state of sync run/stop line 
vel_is_zero:	equ 3		;


hhsel:		equ 4		;
chan_match:	equ 5		;

chksum:		equ 0x1a	;
sustain:	equ 0x1b	;

clk_tmr:	equ h'19'	; used to time width of sync clock pulse
led_tmr:	equ h'20'	; used to count on-time for MIDI activity LED
led_tmr_prev:	equ h'1c'

drum_number:	equ h'21'	

dac_out:	equ h'28'	;

CHANNEL:	equ h'29'	;

button:		equ 1		;Pin on port B connected to pushbutton 

triggers0d:	equ 0x2a
triggers1d:	equ 0x2b

; GPR LOCATIONS 0x30 to 0x3F (+few more) ARE RESERVED FOR DRUM TO NOTE VALUE MAPPING		total 21
; these locations are also used to load in sysex bytes before storage to e2prom




;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;		MACRO DEFINITIONS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

mpx_rimshot	macro 		;set up multiplexers to set rimshot velocity
	movlw b'01111000'	;
	;bsf	PORTB,mpx0a0	;
	;bsf	PORTB,mpx0a1	;
	;bsf	PORTB,mpx0a2	;
	;bsf	PORTB,mpx1a2	;
	endm			;
		
mpx_handclap	macro		;set up multiplexers to set handclap velocity
	movlw b'00011000'	;
	;bsf	PORTB,mpx0a2	;
	;bsf	PORTB,mpx1a2	;
	endm			;

mpx_bassdrum	macro		;bassdrum
	movlw b'00100110'	;
	;bsf	PORTB,mpx0a1	;y2 and y3
	;bsf	PORTB,mpx1a0	;
	;bsf	PORTB,mpx1a1	;
	endm			;

mpx_snaredrum	macro		;snaredrum
	movlw b'00100000'	;
	;bsf	PORTB,mpx0a1	;y2 and y0
	endm			;

mpx_lowtom	macro		;lowtom
	movlw b'00100010'	;
	;bsf	PORTB,mpx0a1	;y2 and y1
	;bsf	PORTB,mpx1a0	;
	endm			;

mpx_midtom	macro		;midtom
	movlw b'00100100'	;
	;bsf	PORTB,mpx0a1	;y2 and y2
	;bsf	PORTB,mpx1a1	;
	endm			;

mpx_hightom	macro		;hightom
	movlw b'00001000'	;
	;bsf	PORTB,mpx1a2	;
	endm			;

mpx_hihat	macro		;hihat
	movlw b'00101010'	;
	;bsf	PORTB,mpx0a1	;
	;bsf	PORTB,mpx1a0	;
	;bsf 	PORTB,mpx1a2	;
	endm			;
	
mpx_crash	macro		;crash
	movlw b'00101100'	;
	;bsf	PORTB,mpx0a1	;
	;bsf	PORTB,mpx1a1	;
	;bsf	PORTB,mpx1a2	;
	endm			;

mpx_ride	macro		;ride
	movlw b'00101110'	;
	;bsf	PORTB,mpx0a1	;
	;bsf	PORTB,mpx1a0	;
	;bsf	PORTB,mpx1a1	;
	;bsf	PORTB,mpx1a2	;
	endm			;


	org h'0000'
	goto begin

        org h'0004'		;interrupt vector.
	goto midi_rx

	org h'0005'		;


default_lookup
	addwf PCL,F		;
	retlw .35		;bassdrum
	retlw .36
	retlw .37		;rimshot
	retlw .38		;snaredrum
	retlw .40
	retlw .39		;handclap
	retlw .39
	retlw .41		;lowtom
	retlw .43
	retlw .42		;closedhat
	retlw .44
	retlw .45		;midtom
	retlw .47
	retlw .46		;openhat
	retlw .46
	retlw .48		;hitom
	retlw .50
	retlw .49		;crash
	retlw .49
	retlw .51		;ride
	retlw .51
	retlw 0			;sustain = 0		

	retlw 0xff		;eolut




main	bcf INTCON,1		;Reset RB0 flag
	bsf INTCON,7		;Global interrupt enable
mainloop
	btfsc INTCON,2
	goto refresh		;Call routine refresh when tmr0 interrupt flag is high. Set to occur every 1.024mS.

	btfsc clk_tmr,7
	clrf clk_tmr
	movf clk_tmr,f
	btfsc STATUS,Z
	bsf PORTB,7

	goto mainloop		;


refresh				;*** Refresh Triggers ***

update_clk			;*** Refresh Sync Clock pulse according to clk_tmr ***

	bcf PORTA,latch0	;ensure 74174(0) clock line is low
	
	btfsc flags,hhsel	;		
	bsf triggers0,2		;
	btfsc flags,sync_runstop;
	bsf triggers0,3		;	

	bsf PORTA,mpx_en	;Multiplexers MUST be inhibited before messing with PortB at all! 
	movlw 0x80		;
	andwf PORTB,F		;
	movf triggers0,W	;
	iorwf triggers0d,W	;
	iorwf PORTB,F		;
	bsf PORTA,latch0	;

	bcf PORTA,latch1	;ensure 74174(1) clock line is low
	movlw 0x80		;
	andwf PORTB,F		;
	movf triggers1,W	;
	iorwf triggers1d,W	;
	iorwf PORTB,F		;
	bsf PORTA,latch1	;

	movf triggers0,W	;Okay for this bit to be interrupted I think, since
	movwf triggers0d	;it does not cause drum to expire early. 
	movf triggers1,W	;
	movwf triggers1d	;

	bcf INTCON,GIE		;Disable interrupts

	clrf triggers0		; Disabling interrupts for this bit should prevent drums expiring too soon. 
	clrf triggers1		;
	bcf INTCON,2		;
	decf clk_tmr,f		;
	bsf INTCON,GIE		;Enable interrupts

update_led			;
	movf led_tmr,f		;
	btfss STATUS,Z		;
	decf led_tmr,f		;
	btfsc STATUS,Z		;
	bsf PORTA,4		;

	goto mainloop		;








								
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;   SERVICE RB0 INTERRUPT - BITBASH RB0 TO GET MIDI BYTE
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;Asynch. MIDI data rate is 32 instruction cycles with 4MHz xtal.
;First bit needs to be polled 48 cycles after RB0 interrupt, 
;Then consecutive bits are polled every 32 cycles. 
;Byte is received LSB first. 					
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
midi_rx:			;


	;originally 5 nops. 

	;with 9 - vel problem even worse

	nop
	nop	
	nop
	nop			; *** DETERMINE A GOOD NUMBER OF NOP'S TO GO HERE ***
	nop
	
	nop
	nop
	nop
	nop	
	nop
	nop
	




	movf led_tmr,W
	movwf led_tmr_prev


	movlw h'ff'		;Reset MIDI Activity LED timer
	movwf led_tmr		;
	
	movlw 0x30		;		9th cycle
	movwf FSR		;

	movlw b'10000000'	;	
	movwf BYTE		;[12nd cycle since entry]
				
loop:				;MIDI BIT POLL LOOP


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; MUST BE 22 CYCLES BETWEEN THESE POINTS

donkeywork:			;'donkeywork' performs some misc. number crunching during the MIDI 
				;bitbash routine - specifically, it is placed within the MIDI byte
				;polling loop. The routine must be 22 cycles long to maintain a delay
				;of exactly 32us between polling. 

	btfss BYTE,0		;Decide what to do. If newbyte LSB is 1 then this is the 8th pass
	goto find_drum_number	;do drum_number mapping 

	;;;;;    VERY MISC. STUFF! - PASS 8 ONLY   ;;;;;;


	clrf dac_out

	btfsc PORTB,7		;Protect the sync clock state on RB7	
	bsf dac_out,7		;

	

;	movf statusbyte,W	;Convert statusbyte to note-off message
;	andlw 0xf0		;if it's a note-on with velocity = 0. 
;	xorlw 0x90		;Doing here saves time in handler routines. 
;	btfss STATUS,Z		;*** SIDE-EFFECT: KILLS NOTES ON KEY 0!!! ***
;	goto cleverbit		;	[8] cycles for this section
;	decf BYTE,W		;If BYTE currently = 00000001, then it will likely be 0 in final pass. 
;cleverbit:			;
;	btfsc STATUS,Z		;
;	bcf statusbyte,4	;


	nop
	nop	
	nop
	nop

	nop
	nop	
	nop
	nop



	swapf drum_number,W	;Some further drum_number processing: drum_number needs to be 
	addlw 0xd1		;swapf'd, and have 0x2f subtracted from it. Then find_drum_number
	movwf drum_number	;is skipped. 
	goto bitbash		;	[5] cycles

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;



find_drum_number		;DO THIS ON PASS 1 TO 7 
	movf INDF,W		;
	xorwf PITCH,W		;
	swapf FSR,W		;
	btfsc STATUS,Z		;
	movwf drum_number	;
	incf FSR,F		;
	movf INDF,W		;
	xorwf PITCH,W		;
	swapf FSR,W		;
	btfsc STATUS,Z		;
	movwf drum_number	;
	incf FSR,F		;
	movf INDF,W		;
	xorwf PITCH,W		;
	swapf FSR,W		;
	btfsc STATUS,Z		;
	movwf drum_number	;
	incf FSR,F		;
	
	bsf PORTA,mpx_en	;	INHIBIT MULTIPLEXERS



	;drum number needs to be initialised with some null value e.g. zero = no drum. or FF. 


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; MUST BE 22 CYCLES BETWEEN THESE POINTS


bitbash:
	bcf STATUS,C		;
	rrf BYTE,F		;
	btfsc PORTB,0		;Bit is tested here - 25th cycle.  		
	bsf BYTE,7		;

	bcf PORTA,dac_en	;Clear DAC strobe line - do here since there was a spare nop sitting around

	movf BYTE,W		;Remember that the final BYTE value will be written to dac out. Hence, 
	iorwf dac_out,F		;set dac_out to 10000000 within the loop on the final pass, and 
				;ior the BYTE data here. MSB of BYTE will be 0. LSB doesn't matter. 
	btfss STATUS,C
	goto loop		;32 cycles inc. goto

	;bcf INTCON,1		;Reset RB0 flag.	;*** THINK ABOUT WHERE TO PUT THIS ***			








									
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;* 			MIDI BYTE PROCESSING				*
;* This routine called if flags,data_waiting = 1, as a result of last	*
;* RB0 interrupt routine. 						*	
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;TIME TAKEN TO EXECUTE MIDI PROCESSING starting at process_midi, inc return back to main:
;-------------------------------------
;Note-on Status Byte: 29 inc return
;
;Note-on pitch data: 26 total. 
;
;Note-on velocity data:  about 33 total. 
;
;




process_midi:			;



;*********************	Status Byte Check  ******************************
;*									*
;************************************************************************

check_for_status:		;Check for a new status byte (MSB of BYTE = 1)
	btfss BYTE,7		;
	goto chk_running_status	;moving swiftly on if not


	movlw h'f8'		;This bit checks for system realtime message (bits f8 are high)	
	subwf BYTE,W		;
	btfsc STATUS,C		;
	goto sys_realtime	;
	
	bcf PORTA,4		;Turn on MIDI Activity LED ** But what if running status? **

	movf BYTE,W		;
	movwf statusbyte	;
	clrf currentbytenum	;must reset currentbytenum to 0 since this indicates last byte received
				;was a status byte.		




;********************* Extract channel number 	*************************									
					
	movlw B'00001111' 	;Extract channel number from Status Byte.
        andwf statusbyte,W	;	
        xorwf CHANNEL,W        ;Compare and clear/set chan_match accordingly

	bsf flags,chan_match	;
	btfsc STATUS,Z		;
	goto lookup		;
	bcf flags,chan_match	;
	goto main		;




;*********************	Running Status Handler **************************
;* If data byte received when status byte was expected (currentbytenum	*
;* =0) then byte is running status. If true, simply increase 		*
;* currentbytenum to 1 and continue as normal using previous known 	*
;* status byte. In unlikely event of there being no status byte ever 	*
;* received (e.g. shortly after powerup) then ignore and exit.		*
;************************************************************************


chk_running_status:		;Check for a data byte when a status byte is expected. 
	movf currentbytenum,F	;See if currentbytenum = 0 (if it is, a status byte is expected)
	btfss STATUS,Z		;
	goto chk_channel_match	;
	incf currentbytenum,F	;Otherwise increase currentbytenum to one, therefore the system uses the last status byte.



chk_channel_match:		;
	btfss flags,chan_match	;
	goto main		;
		





	

;********************* Message type lookup 	*************************
;* Using computed-goto (lookup table) branch to appropriate handler	*
;* depending on the message type. 					*
;************************************************************************

	;*** IDEA: IF VERY FEW MESSAGE TYPES ARE ACTUALLY SUPPORTED, IT MIGHT JUST BE FASTER TO USE 
	;SEVERAL XORWF / BTFSX ETC ETC. *** - extra speed just needed for note-on messages (?)


lookup:				;This routine takes 6 cycles for note on / note off etc. 

	swapf statusbyte,W	;
	andlw 0x0f		;
	addlw 0xf8		;
				;Branch to appropriate message handling 	
        addwf PCL,F             ;routine according to the type of message.
	goto handlenoteoff	;1000   note off		;
        goto handlenoteon     	;1001   note on			;
        goto main;return			;1010   poly key pressure	;
        goto main;return  		;1011   control change		;	
        goto main;return   		;1100   program change		;
        goto main;return			;1101   overall key pressure	;
        goto main;return		 	;1110   pitch wheel		;
	goto main;return			;1111   system message		; *won't actually happen*
			







handlenoteoff: 			;[force appropriate velocity to zero]
	incf currentbytenum,F
	movf currentbytenum,W	;Lookup table branches according to the byte number (current_byte_no)
        addwf PCL,F             ;
	nop
	goto main;return
	goto noteoff_pitch	;
	goto noteoff_velocity	;

noteoff_pitch			;
	movf BYTE,W		;
	movwf PITCH		;
	movlw 0xf2
	movwf drum_number
	goto main;return			;back to main
noteoff_velocity		;
	clrf currentbytenum	;		



	btfsc sustain,6		;Don't do velocity silencing if sustain is less than 64(dec)
	goto main;return			;

noteoff_drum_lookup:		;There are 7*3=21 possible drums 
	
	movf drum_number,W	;
	addwf PCL,F		;
	goto main;return			;drum_number is zero if no match is found in map
	goto main;return;goto noteoff_bassdrum	;
	goto main;return;goto noteoff_bassdrum	;
	goto main;return;goto noteoff_rimshot	;
	goto main;return;goto noteoff_snaredrum	;
	goto main;return;goto noteoff_snaredrum	;
	goto main;return;goto noteoff_handclap	;
	goto main;return;goto noteoff_handclap	;
	goto main;return;goto noteoff_lowtom	;
	goto main;return;goto noteoff_lowtom	;
	goto noteoff_closedhat	;
	goto noteoff_closedhat	;
	goto main;return;goto noteoff_midtom	;
	goto main;return;goto noteoff_midtom	;
	goto noteoff_openhat	;
	goto noteoff_openhat	;
	goto main;return;goto noteoff_hightom	;
	goto main;return;goto noteoff_hightom	;
	goto noteoff_crashcymbal;
	goto noteoff_crashcymbal;
	goto noteoff_ridecymbal	;
	goto noteoff_ridecymbal	;




handlenoteon: 			;**Process Note-On Messages**
	incf currentbytenum,F	;
	movf currentbytenum,W	;Lookup table branches according to the byte number (current_byte_no)
        addwf PCL,F             ;
	nop
	goto main		;
	goto noteon_pitch	;
	goto noteon_velocity	;
noteon_pitch:			;
	movf BYTE,W		;
	movwf PITCH		;Correct drum_number will be magically present in drum_number on next pass!! 
	movlw 0xf2		;will be swapped round to 2f, then 2f subtracted
	movwf drum_number	;
	goto main		;back to main
noteon_velocity:		;

	movf BYTE,F		; *** ADDED 8.12.02 ***
	btfsc STATUS,Z		;
	goto noteoff_velocity	;	

	
	clrf currentbytenum	;


drum_lookup:			;There are 7*3=21 possible drums 
	
	movf drum_number,W	;
	addwf PCL,F		;
	goto main;return			;drum_number is zero if no match is found in map
	goto do_bassdrum	;
	goto do_bassdrum	;
	goto do_rimshot		;
	goto do_snaredrum	;
	goto do_snaredrum	;
	goto do_handclap	;
	goto do_handclap	;
	goto do_lowtom		;
	goto do_lowtom		;
	goto do_closedhat	;
	goto do_closedhat	;
	goto do_midtom		;
	goto do_midtom		;
	goto do_openhat		;
	goto do_openhat		;
	goto do_hightom		;
	goto do_hightom		;
	goto do_crashcymbal	;
	goto do_crashcymbal	;
	goto do_ridecymbal	;
	goto do_ridecymbal	;
		









do_bassdrum:			;
	movf dac_out,W		;dac_out already contains correct setting for RB7. 
	movwf PORTB		;so put it onto PORTB
	bsf PORTA,dac_en	;and load onto DAC. DAC is now ready. 
	movlw 0x80		;Now clear RB1 to RB6, preserving state of RB7
	andwf PORTB,F		;
	mpx_bassdrum		;this macro moves a value into W
	iorwf PORTB,F		;	

	bcf PORTA,mpx_en	;Enable multiplexers
	trig_bassdrum		;
	goto main		;

do_snaredrum:			;
	movf dac_out,W		;dac_out already contains correct setting for RB7. 
	movwf PORTB		;so put it onto PORTB
	bsf PORTA,dac_en	;and load onto DAC. DAC is now ready. 
	movlw 0x80		;Now clear RB1 to RB6, preserving state of RB7
	andwf PORTB,F		;
	mpx_snaredrum		;this macro moves a value into W
	iorwf PORTB,F		;	

	bcf PORTA,mpx_en	;Enable multiplexers
	trig_snaredrum		;
	goto main;eturn			;

do_lowtom:			;
	movf dac_out,W		;dac_out already contains correct setting for RB7. 
	movwf PORTB		;so put it onto PORTB
	bsf PORTA,dac_en	;and load onto DAC. DAC is now ready. 
	movlw 0x80		;Now clear RB1 to RB6, preserving state of RB7
	andwf PORTB,F		;
	mpx_lowtom		;this macro moves a value into W
	iorwf PORTB,F		;	

	bcf PORTA,mpx_en	;Enable multiplexers
	trig_lowtom		;
	goto main;return			;

do_midtom:			;
	movf dac_out,W		;dac_out already contains correct setting for RB7. 
	movwf PORTB		;so put it onto PORTB
	bsf PORTA,dac_en	;and load onto DAC. DAC is now ready. 
	movlw 0x80		;Now clear RB1 to RB6, preserving state of RB7
	andwf PORTB,F		;
	mpx_midtom		;this macro moves a value into W
	iorwf PORTB,F		;	

	bcf PORTA,mpx_en	;Enable multiplexers
	trig_midtom		;
	goto main;return			;

do_hightom:			;
	movf dac_out,W		;dac_out already contains correct setting for RB7. 
	movwf PORTB		;so put it onto PORTB
	bsf PORTA,dac_en	;and load onto DAC. DAC is now ready. 
	movlw 0x80		;Now clear RB1 to RB6, preserving state of RB7
	andwf PORTB,F		;
	mpx_hightom		;this macro moves a value into W
	iorwf PORTB,F		;	

	bcf PORTA,mpx_en	;Enable multiplexers
	trig_hightom		;
	goto main;return			;

do_rimshot:			;
	movf dac_out,W		;dac_out already contains correct setting for RB7. 
	movwf PORTB		;so put it onto PORTB
	bsf PORTA,dac_en	;and load onto DAC. DAC is now ready. 
	movlw 0x80		;Now clear RB1 to RB6, preserving state of RB7
	andwf PORTB,F		;
	mpx_rimshot		;this macro moves a value into W
	iorwf PORTB,F		;	

	bcf PORTA,mpx_en	;Enable multiplexers
	trig_rimshot		;
	goto main;return			;

do_handclap:			;
	movf dac_out,W		;dac_out already contains correct setting for RB7. 
	movwf PORTB		;so put it onto PORTB
	bsf PORTA,dac_en	;and load onto DAC. DAC is now ready. 
	movlw 0x80		;Now clear RB1 to RB6, preserving state of RB7
	andwf PORTB,F		;
	mpx_handclap		;this macro moves a value into W
	iorwf PORTB,F		;	

	bcf PORTA,mpx_en	;Enable multiplexers
	trig_handclap		;
	goto main;return			;

do_closedhat:			;	
	sel_closedhat		;
	movf dac_out,W		;dac_out already contains correct setting for RB7. 
	movwf PORTB		;so put it onto PORTB
	bsf PORTA,dac_en	;and load onto DAC. DAC is now ready. 
	movlw 0x80		;Now clear RB1 to RB6, preserving state of RB7
	andwf PORTB,F		;
	mpx_hihat		;this macro moves a value into W
	iorwf PORTB,F		;	

	bcf PORTA,mpx_en	;Enable multiplexers
	trig_hihat		;
	goto main;return			;


do_openhat:			;
	sel_openhat
	movf dac_out,W		;dac_out already contains correct setting for RB7. 
	movwf PORTB		;so put it onto PORTB
	bsf PORTA,dac_en	;and load onto DAC. DAC is now ready. 
	movlw 0x80		;Now clear RB1 to RB6, preserving state of RB7
	andwf PORTB,F		;
	mpx_hihat		;this macro moves a value into W
	iorwf PORTB,F		;	

	bcf PORTA,mpx_en	;Enable multiplexers
	trig_hihat		;
	goto main;return			;

do_crashcymbal:			;
	movf dac_out,W		;dac_out already contains correct setting for RB7. 
	movwf PORTB		;so put it onto PORTB
	bsf PORTA,dac_en	;and load onto DAC. DAC is now ready. 
	movlw 0x80		;Now clear RB1 to RB6, preserving state of RB7
	andwf PORTB,F		;
	mpx_crash		;this macro moves a value into W
	iorwf PORTB,F		;	

	bcf PORTA,mpx_en	;Enable multiplexers
	trig_crash		;
	goto main;return			;

do_ridecymbal:			;
	movf dac_out,W		;dac_out already contains correct setting for RB7. 
	movwf PORTB		;so put it onto PORTB
	bsf PORTA,dac_en	;and load onto DAC. DAC is now ready. 
	movlw 0x80		;Now clear RB1 to RB6, preserving state of RB7
	andwf PORTB,F		;
	mpx_ride		;this macro moves a value into W
	iorwf PORTB,F		;	

	bcf PORTA,mpx_en	;Enable multiplexers
	trig_ride		;
	goto main;return			;





noteoff_closedhat:		;	
	movlw 0x80		;
	andwf PORTB,F		;
	bsf PORTA,dac_en	;
	mpx_hihat		;
	iorwf PORTB,F		;
	bcf PORTA,mpx_en	;
	goto main;return			;


noteoff_openhat:		;
	movlw 0x80		;
	andwf PORTB,F		;
	bsf PORTA,dac_en	;
	mpx_hihat		;
	iorwf PORTB,F		;
	bcf PORTA,mpx_en	;
	goto main;return			;

noteoff_crashcymbal:		;
	movlw 0x80		;
	andwf PORTB,F		;
	bsf PORTA,dac_en	;
	mpx_crash		;
	iorwf PORTB,F		;
	bcf PORTA,mpx_en	;
	goto main;return			;

noteoff_ridecymbal:		;
	movlw 0x80		;
	andwf PORTB,F		;
	bsf PORTA,dac_en	;
	mpx_ride		;
	iorwf PORTB,F		;
	bcf PORTA,mpx_en	;
	goto main;return			;




sys_realtime:			;***handle realtime messages for midi>sync conversion***
	
	movf led_tmr_prev,W	;Don't want the MIDI activity LED to respond to realtime messages,
	movwf led_tmr		;so restore original led_tmr value

check_start			;See if realtime message is 'start'
	movlw h'FA'		;Compare with 0xFA
	xorwf BYTE,W		;
	btfss STATUS,Z		;
	goto check_stop		;
	bcf flags,sync_runstop	;External sync run/stop line will go high
	goto main;return			;Back to main
check_stop			;See if realtime message is 'stop'
	movlw h'FC'		;Compare with 0xFC
	xorwf BYTE,W		;
	btfss STATUS,Z		;
	goto check_clock	;
	bsf flags,sync_runstop	;External sync run/stop line will go low
	goto main;return			;Back to main
check_clock			;See if realtime message is 'clock' 
	movlw h'F8'		;Compare with 0xF8
	xorwf BYTE,W		;
	btfss STATUS,Z		;
	goto main;return			;Back to main
	bcf PORTB,7		;Set the clock line high,

	movlw 0x05;06		;and reset clock pulse timer.
	movwf clk_tmr		;
	goto main;return			;Back to main





begin:

	clrf triggers0		;
	clrf triggers1		;
	clrf triggers0d		;
	clrf triggers1d		;
	clrf flags		;
	clrf currentbytenum	;
	clrf clk_tmr		;
	bsf flags,sync_runstop	;
	

	clrf PORTA		;
	clrf PORTB		;
	PAGE1			;
	movlw b'00000001'	;All of port B = outputs except for RB0 is MIDI input. 
        movwf TRISB             ;	
        clrf TRISA		;All of port A = outputs. 
        PAGE0			;


	bsf PORTA,latch0	;
	movlw 0xff		;
	bsf PORTA,latch1	;
	movwf led_tmr		;led_tmr = ff so that MIDI LED lights briefly upon startup
	bcf PORTA,4		;
	

        PAGE1			;
	movlw b'00000011'	;All of port B = outputs except for RB0 is MIDI input, RB1 for button input
        movwf TRISB             ;	  
	movlw b'00000001'	;Enable PORTB pullups, set prescaler to 1:4, assign prescaler to TMR0, 
	movwf OPTIONREG		;and RB0 interrupt upon high-to-low edge. 
	bcf EECON1,EEIF		;Clear eeprom write interrupt flag
        PAGE0			;


	bcf INTCON,2		;clear T0IF. (must be done in software-see datasheet, INTCON register)
	bsf INTCON,4		;enable RB0 interrupt	
	bcf INTCON,1		;Reset RB0 flag	
	bsf PORTA,mpx_en	;inhibit demultiplexers
	bcf INTCON,7		;global interrupt disabled for now
	bsf PORTA,dac_en	;DAC write disabled (latching)	


	movlw b'10100000'	;
	movwf statusbyte	;

	
	btfss PORTB,1		;See if the learn/sysex button is being pressed on startup. If so,
	goto sysex		;go to sysex routines. 

done_sysex


	clrf PORTB		;
	PAGE1			;
	movlw b'00000001'	;All of port B = outputs except for RB0 is MIDI input. 
        movwf TRISB             ;	

        PAGE0			;



	goto restore		;













sysex	btfsc led_tmr,7		;Flash LED at high rate to indicate waiting for data	
	bcf PORTA,4		;
	btfss led_tmr,7		; 						
	bsf PORTA,4		;
	btfsc INTCON,1		; 							  
	goto getstat		; 
	btfss INTCON,2		;
	goto sysex		;
	bcf INTCON,2		;
	decf led_tmr,F		;								 
	goto sysex		;
							
getstat	call rx			;Go and get the first byte, which should be a status byte
				;Now want to determine what was received
	xorlw 0xf0		;compare with F0 - start sysex indicator
	btfsc STATUS,Z		;
	goto get_identifier	;jump to receive further sysex data bytes if true
	andlw 0xf0		;
	xorlw b'01100000'	;
	btfsc STATUS,Z		;If it's a note-on message, jump to learn_channel
	goto learn_channel	;
	goto sysex		;Otherwise go back, carry on flashing LED





get_identifier			;look for 00 90 90

	call rx			;
	xorlw 0x00		;
	btfss STATUS,Z		;
	goto sysex		;
	call rx			;
	xorlw 0x90		;
	btfss STATUS,Z		;
	goto sysex		;
	call rx			;
	xorlw 0x90		;
	btfss STATUS,Z		;
	goto sysex		;


	
get_sysex			;come here if F0 00 90 90 sequence recieved
	bcf PORTA,4		;Keep the active LED illuminated
	movlw 0x30		;
	movwf FSR		;

sysex_wait			;Need to implement a timeout feature??
	call rx			;

	movlw h'f8'		;Trap and ignore realtime clock messages. 
	subwf BYTE,W		;FA and FC (start /stop) messages are not caught; these
	btfsc STATUS,C		;will cause the sysex process to abort and restart
	goto sysex_wait		;

	btfsc BYTE,7		;check that it's not a statusbyte
	goto sysex		;restart if error
	movf BYTE,W		;
	movwf INDF		;
	incf FSR,F		;
	movf FSR,W		;
	xorlw 0x46		;Need to receive 22(dec) data bytes. 22dec + 30h = 46h --> exit when reached this value
	btfss STATUS,Z		;
	goto sysex_wait		;			




store	movlw 0x30		;///// STORE DATA HELD IN 0X30 UPWARDS TO EEPROM /////
	movwf FSR		;
	clrf EEADR		;Start at location 00h
	clrf chksum		;
stloop	movf INDF,W		;
	movwf EEDATA		;
	addwf chksum,F		;
	call eeprom_wr		;
	incf EEADR,F		;
	incf FSR,F		;
	movf FSR,W		;
	xorlw 0x46		;Need to store 22(dec) data bytes. 22dec + 30h = 46h --> exit when reached this value
	btfss STATUS,Z		;
	goto stloop		;

	movf chksum,W		;Store checksum into 16h
	movwf EEDATA		;
	call eeprom_wr		;
	comf chksum,W		;Store comf'd checksum into 17h
	movwf EEDATA		;
	incf EEADR,F		;
	call eeprom_wr		;	
	call led_ok		;
	goto done_sysex		;







learn_channel			;
	movlw 0x18		;
	movwf EEADR		;
	movlw 0x0f		;
	andwf BYTE,W		;
	movwf EEDATA		;
	call eeprom_wr		;
	call led_ok		;
	goto done_sysex		;



led_ok				;
	movlw 0x04		;
	movwf TEMP0		;
	clrf led_tmr		;
led_ok_loop			;
	movf led_tmr,W		;
	subwf TMR0,W		;
	btfsc TEMP0,2		;
	bsf STATUS,C		;
	btfss STATUS,C		;
	bcf PORTA,4		;
	btfsc STATUS,C		;
	bsf PORTA,4		;
	btfss INTCON,2		;
	goto led_ok_loop	;
	bcf INTCON,2		;
	decf led_tmr,F		;
	btfsc STATUS,Z		;
	decf TEMP0,F		;
	btfss STATUS,Z		;
	goto led_ok_loop	;
	bsf PORTA,4		;
	return
	





rx	btfss INTCON,1		;
	goto rx			;[2] if interrupt happened here or otherwise [5 or 6]

	movlw 0x03		; \
	movwf TEMP1		;   10 cycles
delay1	decfsz TEMP1,f		;
	goto delay1		; /

	movlw b'10000000'	;	
	movwf BYTE		;[14th cycle since RB0 edge... approx]

loop1	movlw 0x08		; \ delay is initial 2, then this value-1   x 3,  then + 2
	movwf TEMP1		;   i.e. value*3 + 1
delay2	decfsz TEMP1,f		;   need 25
	goto delay2		; / 

	bcf STATUS,C		;
	rrf BYTE,F		;
	btfsc PORTB,0		;Bit is tested here - 42nd cycle.  		
	bsf BYTE,7		;
	btfss STATUS,C		;
	goto loop1		;32 cycles inc. goto
	bcf INTCON,1		;
	movf BYTE,W		;
	return			;
	



eeprom_wr			;/// write data to EEPROM ///
	PAGE1			;
	bsf EECON1,WREN		;
	movlw 0x55		;Special EECON2 sequence
	movwf EECON2		;
	movlw 0xaa		;
	movwf EECON2		;
	bsf EECON1,WR		;
eifwait	btfss EECON1,EEIF	;
	goto eifwait		;
	bcf EECON1,EEIF		;
	bcf EECON1,WREN		;
	PAGE0			;
	return			;


eeprom_rd			;/// read data from EEPROM ///
	PAGE1			;
	bsf EECON1,RD		;
	PAGE0			;
	return			;
	




restore				;
	;/// drum map information is stored in 21 eeprom locations, starting at eeprom 00h ///
	clrf EEADR		;
	movlw 0x30		;
	movwf FSR		;
	clrf chksum		;
restore_loop		
	call eeprom_rd		;
	movf EEDATA,W		;
	addwf chksum,F		;
	movwf INDF		;
	incf EEADR,F		;
	incf FSR,F		;
	movf FSR,W		;
	xorlw 0x45		;doing locations 30 to 44 h (21 locations), hence if FSR=45h then exit. 	
	btfss STATUS,Z		;
	goto restore_loop	;

	call eeprom_rd		;
	movf EEDATA,W		;

	movwf sustain		;

	addwf chksum,F		;
	movf chksum,W		;
	incf EEADR,F		;
	call eeprom_rd		;
	xorwf EEDATA,W		;
	btfss STATUS,Z		;
	goto default		;
	comf chksum,W		;
	incf EEADR,F		;
	call eeprom_rd		;
	xorwf EEDATA,W		;
	btfss STATUS,Z		;
	goto default		;

	incf EEADR,F		;
	call eeprom_rd		;
	movf EEDATA,W		;Read channel from this location...
	movwf CHANNEL		;..and store to CHANNEL.
	goto main		;




default				;
	movlw 0x18		;
	movwf EEADR		;
	movlw 0x09		;09h = channel 10
	movwf EEDATA		;
	call eeprom_wr		;

	movlw 0x30		;
	movwf FSR		;
default_loop			;
	movlw 0x30		;
	subwf FSR,W		;
	call default_lookup	;
	movwf INDF		;	
	xorlw 0xff		;
	btfsc STATUS,Z		;
	goto store		;
	incf FSR,F		;
	goto default_loop	;

	




	end			;That's all folks. 

