;------------------------------------------------------------------
; FC-50 foot controller
;
; Version 1.0 (NOV-2000 Joris van den Heuvel)
; No interrupt routine
; Static led indication
; MIDI channels 1-4
;
;------------------------------------------------------------------
; Direction for use:
;
; The program change number sent is calculated as follows:
; program = 12*range + 4*bank + preset
; giving control of prgrams 1-48
;
; User interface:
;-------------|----------------------------------------------------
; Key number  |	Action
;-------------|----------------------------------------------------
; 1-4		send preset 1-4
; 1+2		select bank 1
; 2+3		select bank 2
; 3+4		select bank 3
; 1+4		select range
;		press 1-4 for range 1-4
; 1+2+3		bank reset on range select
; 		1 no reset
;		  selecting a new range doesn't change bank and preset
; 		2 bank reset (power-on default)
;		  selecting a new range resets the bank nr but doesn't send
;		  a new preset
; 		3 same as 1
; 		4 bank + preset reset
;		  selecting a new range resets the bank nr and sends preset 1
; 1+2+3+4	select MIDI channel
;		press 1-4 for MIDI channel 1-4
; 2+3+4		reset unit, confirm with pressing 1+2+3+4
;		The unit acts as if power was just applied
;
;------------------------------------------------------------------

	processor 16F84A
	include	  <P16F84A.INC>
	__config  _XT_OSC & _WDT_OFF & _PWRTE_ON

;------------------------------------------------------------------
; declare variables
;------------------------------------------------------------------
t	equ	D'0230'		;timer reload value
midi	equ	H'0004'		;port A MIDI bit nr
range	equ	H'0020'		;range 1-4 (0-3)
bnk	equ	H'0021'		;bank 1-3 (0-2)
preset	equ	H'0022'		;preset 1-4 (0-3)
kpressd	equ	H'0023'		;key pressed
channel	equ	H'0024'		;MIDI channel
temp	equ	H'0025'		;temp register
cnt	equ	H'0026'		;counter temp
program	equ	H'0027'		;PROGRAM number
ledtemp	equ	H'0028'		;temp for preset led directly from keys
leds	equ	H'0029'		;current preset led
resetct	equ	H'002A'		;reset bank/preset on range change
;------------------------------------------------------------------
; PROGRAM START
;------------------------------------------------------------------
start	org	H'0000'		;start at PCL 0000
	goto	init		;jump to program start
irq	org	H'0004'
	goto	isr		;interrupt vector at PCL 0004
;------------------------------------------------------------------
; INITIALIZATION
;------------------------------------------------------------------
init	bsf	STATUS,RP0	;switch to register bank 1
	movlw	B'00000000'	;set port B as output
	movwf	TRISB
	movlw	B'00001111'	;set port A bit 4 as output, bits 3:0 as input
	movwf	TRISA
	bcf	OPTION_REG,T0CS	;start timer
	bcf	STATUS,RP0	;switch to register bank 0
	bsf	PORTA,midi	;clear MIDI line (stop bit = 1)
	movlw	H'20'		;load address 0020
	movwf	FSR		;into FSR
clear	clrf	INDF		;clear indirect RAM address
	incf	FSR,F
	btfss	FSR,6		;reached address 0040? (bit B'01000000' set?)
	goto	clear		;repeat
	incf	resetct,F	;set reset mode to "reset bank"
ledtest	movlw	B'11110000'	;all leds red
	movwf	PORTB
	call	blink		;blink
	swapf	PORTB,F		;all leds green
	call	blink
	clrf	PORTB
	call	wait
	bsf	STATUS,C
testlp	rlf	PORTB,F		;rotate through all 8 leds
	call	wait
	btfss	STATUS,C	;done 8 leds?
	goto	testlp
	clrf	PORTB		;all leds off
;------------------------------------------------------------------
; MAIN LOOP
;------------------------------------------------------------------
waitkey	call	getkey		;stand-by wait loop
	movf	kpressd,W	;copy keycode
	movwf	ledtemp		; to led temp
kdecode	bcf	STATUS,C	;clear carry
	rlf	kpressd,W	;load 2 x kpressd into W
	addwf	PCL,F		;jump table
	nop	
	goto	waitkey		;safety net
	movlw	d'0'
	goto	spreset		;0001	preset 1
	movlw	d'1'
	goto	spreset		;0010	preset 2
	movlw	d'0'
	goto	setbk		;0011	bank 1
	movlw	d'2'
	goto	spreset		;0100	preset 3
	nop
	goto	waitkey		;0101	-
	movlw	d'1'
	goto	setbk		;0110	bank 2
	nop
	goto	resetbk		;0111	reset bank
	movlw	d'3'
	goto	spreset		;1000	preset 4
	nop
	goto	setrg		;1001	select range 1-4
	nop
	goto	waitkey		;1010	-
	nop
	goto	waitkey		;1011	-
	movlw	d'2'
	goto	setbk		;1100	bank 3
	nop
	goto	waitkey		;1101	-
	nop	
	goto	resetnt		;1110	reset unit
	nop
	goto	setch		;1111	select MIDI channel 1-4
;------------------------------------------------------------------
; SET MIDI CHANNEL (not subroutine)
;------------------------------------------------------------------
setch	movlw	B'11110000'	;all leds red
	movwf	PORTB
	call	get1key		;get channel 1-4
	movwf	channel
	goto	goback		;finish
;------------------------------------------------------------------
; SET RANGE (not subroutine)
;------------------------------------------------------------------
setrg	movlw	B'10010110'	;led 1 red 2 grn 3 grn 4 red
	movwf	PORTB
	call 	get1key		;get range 1-4
	movwf	range
	call	blink
	btfss	resetct,0	;check reset bank bit
	goto	gbnobl		;no, go back without blinking
	clrf	bnk		;select bank 1
	clrf	PORTB		;return with leds off
	clrf	leds		;make sure they stay off
	btfss	resetct,1	;check reset preset bit
	goto	waitkey		;no, go back
	movlw	B'00000001'	;yes, simulate key 1 was pressed
	movwf	ledtemp
	clrw			;load W with preset 1 (=0)
	goto	spreset		;send preset 1 bank 1
;------------------------------------------------------------------
; ENABLE RESET BANK/PRESET ON RANGE SELECT (not subroutine)
;------------------------------------------------------------------
resetbk	movlw	B'01111000'	;led 1-2-3 red 4 green
	movwf	PORTB
	call	get1key		;get reset mode
	movwf	resetct
	goto	goback
;------------------------------------------------------------------
; RESET UNIT CONFIRM
;------------------------------------------------------------------
resetnt	movlw	B'11100001'	;led 1 green 2-3-4 red
	movwf	PORTB
	call	getkey
	bcf	STATUS,C
	movf	kpressd,W	;get key combination
	sublw	H'0F'		;kpressed = B'00001111' ?
	btfsc	STATUS,Z
	goto	init		;yes
	goto	goback		;no
;------------------------------------------------------------------
; SET BANK (not subroutine)
;------------------------------------------------------------------
setbk	movwf	bnk		;store new bank nr
	movlw	B'00000001'	;bank 1 : led 1 green
	btfsc	bnk,0
	movlw	B'00110011'	;bank 2 : led 1, 2 orange
	btfsc	bnk,1
	movlw	B'01110000'	;bank 3 : led 1, 2, 3 red
	movwf	PORTB
goback	call	blink		;blink leds
gbnobl	movf	leds,W		;return leds to previous state
	movwf	PORTB
	goto	waitkey
;------------------------------------------------------------------
; SET AND SEND PRESET (not subroutine)
;------------------------------------------------------------------
spreset	movwf	preset		;store new preset nr
	movf	bnk,W		;get bank nr
	btfsc	STATUS,Z	;bank 1?
	goto	cont		;yes, leave green led as it is
	movf	ledtemp,W
	swapf	ledtemp,F	;not bank 1, switch green led to red
	btfsc	bnk,0		;bank 2?
	iorwf	ledtemp,F	;yes, copy red back to green
cont	movf	ledtemp,W	;no, continue and
	movwf	leds		; copy temp to permanent register
	movwf	PORTB		;turn led on
;------------------------------------------------------------------
; CALCULATE PROGRAM FROM RANGE, BANK and PRESET
;------------------------------------------------------------------
calcpg	bcf	STATUS,C
	movf	range,W		;get range nr
	addwf	range,W	
	addwf	range,W		;W = 3 * range
	movwf	temp		;temp = 3 * range
	rlf	temp,F
	rlf	temp,W		;W = temp * 4
	movwf	program
	movf	bnk,W		;get bank nr
	movwf	temp
	rlf	temp,F
	rlf	temp,W		;W = 4 * bank
	addwf	preset,W	;W = W + preset
	addwf	program,F	;program = 12* range + 4* bank + preset
sprch	movf	channel,W	;get channel nr
	iorlw	H'C0'		;add PROGRAM CHANGE opcode
	call	sendbte		;send MIDI PC status byte
	movf	program,W	;get program nr
	call	sendbte		;send MIDI PC data byte
	goto	waitkey
;------------------------------------------------------------------
; GET KEY (COMBINATION) AND RETURN KEYCODE IN KPRESSD
;------------------------------------------------------------------
getkey	clrf	kpressd		;kpressd = 0
waitrel	movf	PORTA,W		;WAIT FOR KEY RELEASE (SAFETY BARRIER)
	andlw	H'0F'		;filter bits 3:0
	btfss	STATUS,Z	;if w = 0 then skip
	goto	waitrel
waitprs	movf	PORTA,W		;WAIT FOR KEYPRESS
	andlw	H'0F'		;filter bits 3:0
	btfsc	STATUS,Z	;if w <> 0 then skip
	goto	waitprs
keydlay movf	PORTA,W		;GET COMBINATION OF KEYS AND WAIT FOR RELEASE
	andlw	H'0F'		;filter bits 3:0
	btfsc	STATUS,Z	;if w = 0 then return
	return
	iorwf	kpressd,F	;kpressd = kpressd OR w
	goto	keydlay
;------------------------------------------------------------------
; WAIT FOR 1 KEYPRESS AND RETURN KEY NUMBER IN W
;------------------------------------------------------------------
get1key	movf	PORTA,W		;WAIT FOR KEY
	andlw	H'0F'
	btfsc	STATUS,Z	;if key (w not 0) then skip
	goto	get1key
	movwf	temp
	movwf	PORTB		;key number led lights green
	bcf	STATUS,C	;convert keycode to keynumber
	rrf	temp,F		;shift keycode right
	movf	temp,W		;load key (for key 1/2/3) into W
	btfsc	temp,2		;key 4 pressed?
	movlw	D'3'		;yes, load keynr 4
	return			;no, return
;------------------------------------------------------------------
; SEND MIDI BYTE IN W
;------------------------------------------------------------------
sendbte movwf	temp		;store byte to send in temp
	call	timeout
	movlw	9		;reset bit counter
	movwf	cnt
	bcf	PORTA,midi	;set start bit
	bsf	STATUS,C	;set carry (stop bit)
tloop	call	timeout		;wait for time out
	rrf	temp,F		;rotate next LSB into C
	btfsc	STATUS,C
	goto	send1
send0	bcf	PORTA,midi
	goto	nxtbit
send1	bsf	PORTA,midi
nxtbit	decfsz	cnt,F		;all 8 bits + stop sent?
	goto	tloop		;no, next bit
	return
;------------------------------------------------------------------
; WAIT FOR TMR0 TIME OUT AND START TIMER AGAIN
;------------------------------------------------------------------
timeout	btfss	INTCON,T0IF
	goto	timeout
	movlw	t
	movwf	TMR0
	bcf	INTCON,T0IF
	return
;------------------------------------------------------------------
; BLINK LEDS
;------------------------------------------------------------------
blink	movf	PORTB,W
	call	wait
	clrf	PORTB
	call	wait
	movwf	PORTB
	call	wait
	clrf	PORTB
	call	wait
	movwf	PORTB
	call	wait
	return
;------------------------------------------------------------------
; WAIT LOOP 0.3 SECONDS
;------------------------------------------------------------------
wait	clrf	cnt
	clrf	temp
loop	incfsz	temp,F
	goto	loop
	decfsz	cnt,F
	goto	loop
	return
;------------------------------------------------------------------
; INTERRUPT SERVICE ROUTINE
;------------------------------------------------------------------
isr	retfie			;interrupt not used
;------------------------------------------------------------------
	end
;------------------------------------------------------------------
		