;
;=========================
;	PIC-MIDI-LED
;=========================
;
;	Copyright 2007 アルゴ算法堂
;
;
;	LEDのオンオフをMIDIで制御
;	Clock4MHz Internal 版 
;
;	更新履歴
;	=======
;	07/07/03	Ver.1.00	id=0x200
;
;	内蔵オシレータではMCLRをGPIOとして使用できない。
;
	list	 p=16f648a
	#include p16f648a.inc
	
; CONFIGRATIONS
	__config	_INTOSC_OSC_NOCLKOUT & _PWRTE_ON & _WDT_OFF & _BOREN_ON & _LVP_OFF & _MCLRE_ON & _DATA_CP_OFF & _CP_OFF


; Set User ID Memory
	__idlocs	0x0200
;
;
; constant
;
option_val	equ	b'10010111'
	;b7		RPBU	1:nouse pullup
	;b6		INTEDE	0:nouse	rb0/int
	;b5		T0CS	timer0 clock source 0:FOSC/4
	;b4		T0SE	nouse	timer0 clock edge
	;b3		PSA		0: prescaler to timer0
	;b2-b0	PSn		divider
	;				0:	1:2
	;				1:	1:4
	;				2:	1:8
	;				3:	1:16
	;				4:	1:32
	;				5:	1:64
	;				6:	1:128
	;				7:	1:255
;
;
;
; IO BIT INDEX
PORTA_LED_CIS	equ	0	;OUT
PORTA_LED_C	equ	1	;OUT
PORTA_LED_B	equ	2	;OUT
PORTA_LED_AIS	equ	3	;OUT
PORTA_LED_ERROR	equ	4	;OUT	論理が逆で470オームを介して電源に繋ぐ
PORTA_LED_DIS	equ	6	;OUT
PORTA_LED_D	equ	7	;OUT
;
PORTB_LED_A	equ	0	;OUT
PORTB_LED_GIS	equ	3	;OUT
PORTB_LED_G	equ	4	;OUT
PORTB_LED_FIS	equ	5	;OUT
PORTB_LED_F	equ	6	;OUT
PORTB_LED_E	equ	7	;OUT
;
PORTA_LED_ALL	equ	b'11001111'	; error ledを除く
PORTB_LED_ALL	equ	b'11111001'
;
; COMMON REGISTERS (NON BANKED REGISTERS)
;
	cblock	0x70
	irq_wback	; wk backup
	irq_sback	; swap backup
	irq_fsrback
	irq_pclathback
	endc
;
; bank0
;	banked works
;
	cblock	0x20
;
	temp			; temporary work for general
;
;	tmr0_count		; notuse
;
;-- general bit status
	basenote		; 最低音
;
;-- gpio bacup ----
;
	porta_back
	portb_back
; --
	curnote
;
; -- MidiIn_works --
;
	MidiIn_Channel	;;rcv channel predefined
    ;;
	MidiIn_flag		;;
	MidiIn_temp
	MidiIn_a
	MidiIn_b
	MidiIn_c
; -- MidiIn_works End --
;
; rpn 関係
	MidiInRender_rpnL
	MidiInRender_rpnH
;
	endc
;
; MIDIIN_FLAG BIT ASSIGN
;
	cblock	0
	MIDIIN_FLAG_3RD
	MIDIIN_FLAG_ERR
	endc
;
;
;	EEPROM Assign
;
	cblock	0
	EE_CHANNEL			;MIDI チャンネル
	EE_BASENOTE			;最低音情報
	endc
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;	programs
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	org	0x0
;
;***************
;	startup
;***************
startup:
	call	initialize
	goto	main
;
;
	org	0x4
;
;***************
;	intrupt
;***************
irq:
	; prologue
	movwf	irq_wback	;save wk
	swapf	STATUS,w	;save status
	movwf	irq_sback
;
	bcf	STATUS,RP0
	bcf	STATUS,RP1
	bcf	STATUS,IRP
	;
	; prologue 2
	movf	FSR,w
	movwf	irq_fsrback
	movf	PCLATH,w
	movwf	irq_pclathback
	;
	;----------------
	btfsc	PIR1,RCIF
	call	rsirq
	;----------------
	; epilogue 2
	;
	movf	irq_pclathback,w
	movwf	PCLATH
	movf	irq_fsrback,w
	movwf	FSR
	;
	; epilogue
	swapf	irq_sback,w	;load status
	movwf	STATUS
	swapf	irq_wback,f	;load wk
	swapf	irq_wback,w
	retfie


;;;;;;;;;;;;
;***********************************
;	initialize for standard hardware
;***********************************
initialize:
	clrf	STATUS
;
; initialize portA & portB
;
	movlw	(1<<PORTA_LED_ERROR)
	movwf	PORTA
	movwf	porta_back
	clrw
	movwf	PORTB
	movwf	portb_back
;
; data directions
;
	bsf	STATUS,RP0
	movlw	b'00100000'
			; b0: 0 GPIO Output LED Drive
			; b1: 0 GPIO Output LED Drive
			; b2: 0 GPIO Output LED Drive
			; b3: 0 GPIO Output LED Drive
			; b4: 0 GPIO Output Error LED
			; b5: 1 GPIO Input SwRead
			; b6: 0 GPIO Output LED Drive
			; b7: 0 GPIO Output LED Drive
	movwf	TRISA
	movlw	b'00000110'
			; b0: 0 GPIO Input DipSw
			; b1: 1 RX (must be 1)
			; b2: 1 TX (must be 1)
			; b3: 0 GPIO Output LED Drive
			; b4: 0 GPIO Output LED Drive
			; b5: 0 GPIO Output LED Drive
			; b6: 0 GPIO Output LED Drive
			; b7: 0 GPIO Output LED Drive
	movwf	TRISB
	bcf	STATUS,RP0
;
; option_reg
;
	bsf	STATUS,RP0
	movlw	option_val
	movwf	OPTION_REG
	bcf	STATUS,RP0
;
; ccp nouse (default)
;
; comparator(not used)
;
	movlw	0x07		; com off
	movwf	CMCON
;
; vref nouse (default)
; timer1(not used) default
; timer2(not used) default
;
; usart
	movlw	b'00100100'	;txen,brgh
	bsf	STATUS,RP0
	movwf	TXSTA
	movlw	b'10010000'	;spen,cren
	bcf	STATUS,RP0
	movwf	RCSTA
	movlw	0x7		; for 4MHz
	bsf	STATUS,RP0
	movwf	SPBRG
	bsf	PIE1,RCIE	;enable interupt
	bcf	STATUS,RP0
;	movf	RCREG,w		; boodoo
;
; timer0 nouse
;
	clrf	TMR0
; interupt
	clrf	INTCON
	return
;
;****************
;	main routine
;****************

main:
;
; EEPROMを読む
;
	movlw	EE_CHANNEL
	call	ReadRom
	andlw	0x0f
	movwf	MidiIn_Channel
;
	movlw	EE_BASENOTE
	CALL	ReadRom
	movwf	basenote
;
;	ハード周りのとりあえず初期化
;
	call	MidiInit
	call	MidiInRenderInit
;
	bsf	INTCON,PEIE		; all peri irq
	bsf	INTCON,GIE		; global irq
;
;
main_loop:
;
	nop
	goto	main_loop
;
;----------------
; rs irq routine
;----------------
rsirq:
	bsf	porta_back,PORTA_LED_ERROR
	movf	porta_back,w
	movwf	PORTA
;
	btfsc	RCSTA,FERR
	goto	rsirq_f_err
	btfsc	RCSTA,OERR
	goto	rsirq_o_err
	movf	RCREG,w		;read & clear ferr
	goto	MidiIn
rsirq_f_err:
	movf	RCREG,w
	bsf	MidiIn_flag,MIDIIN_FLAG_ERR
	goto	rsirq_error
rsirq_o_err:
	bcf	RCSTA,CREN	;overun_error
	bsf	RCSTA,CREN
	bsf	MidiIn_flag,MIDIIN_FLAG_ERR
rsirq_error:
	bcf	porta_back,PORTA_LED_ERROR
	movf	porta_back,w
	movwf	PORTA
	return			; ignore error process


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;=======================
; Phisical Layer
;=======================
;


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;======================
;	MIDIin
;======================
;
;--------/ MidiInit /--------
;
;	初期化
;
MidiInit:
	clrf	MidiIn_a
	bcf	MidiIn_flag,MIDIIN_FLAG_3RD
	return
;
;--------/ MidiIn /----------
;
;	byte 2 msg conversion
;
;	in:	w:	data byte
;	out:	MidiIn_a: 1st bytes
;			MidiIn_b: 2nd bytes
;			MidiIn_c: 3rd bytes
;
MidiIn:
;
	movwf	MidiIn_temp
	btfss	MidiIn_temp,7
	goto	MidiIn_data
MidiIn_status:
	movlw	0xf8
	subwf	MidiIn_temp,w
	btfsc	STATUS,C
	return					; SystemRealMsg
	bcf	MidiIn_flag,MIDIIN_FLAG_3RD	; clear 3rd byte flag
	movf	MidiIn_temp,w
	movwf	MidiIn_a
;
	movlw	0xf0
	subwf	MidiIn_temp,w
	btfsc	STATUS,C
	clrf	MidiIn_a		; ignore system message
	return
;		
MidiIn_data:					; data value
	btfss	MidiIn_flag,MIDIIN_FLAG_3RD	; 3rd byte flag test
	goto	MidiIn_cont
;;	movf	MidiIn_temp,w
	movwf	MidiIn_c
	bcf	MidiIn_flag,MIDIIN_FLAG_3RD	; 3rd byte flag clear
	goto	MidiInRender	; msg set (call & return)
;
MidiIn_cont:
	movf	MidiIn_a,f
	btfsc	STATUS,Z
	return					; status = 0: ignore failure data
;;;	movf	MidiIn_temp,w
	movwf	MidiIn_b
	movlw	0xc0
	subwf	MidiIn_a,w
	btfss	STATUS,C
	goto	MidiIn_Ch3			; 0x8n,0x9n,0xAn,0xBn
	movlw	0xe0
	subwf	MidiIn_a,w
	btfss	STATUS,C
	goto	MidiInRender		; 2byte msg: 0xCn,0xDn ( call & return )
MidiIn_Ch3:
	bsf	MidiIn_flag,MIDIIN_FLAG_3RD
	return
;
;
;--------------------------------------------------------
;
;
;
;--------/ MidiInRenderInit /--------
;
MidiInRenderInit
	return

;--------/ MidiInRender /--------
;
;	action routine
;
;	直接発音させるか、バッファに詰めるかのなどの処理。
;
;	現在は直接アクセス、即時処理とする
;
;	in:	MidiIn_a:
;		MidiIn_b:
;		MidiIn_c:
;
MidiInRender:
;
; CHANNEL FILTER
;
	movlw	0x0f
	andwf	MidiIn_a,w
	subwf	MidiIn_Channel,w
	btfss	STATUS,Z
	return
;
;	jump to Msg Handler
;
	movlw	high MidiInRender_Vec
	movwf	PCLATH
	swapf	MidiIn_a,w
	andlw	0x7
	addlw	low MidiInRender_Vec
	movwf	PCL
		
;
; Ctrl Chng
;
;0xbn cc
MidiInRender_CC:			;control changes
	movlw	.120
	subwf	MidiIn_b,w
	btfss	STATUS,C
	return				; 0-119 :ignore
	movlw	.121
	subwf	MidiIn_b,w
	btfsc	STATUS,Z
	return				; 121: reset all controler
	goto	AllNoteOff	; call & return
;
; Note On Msg
;
MidiInRender_NoteOnP:
	movf	MidiIn_c,f
	btfss	STATUS,Z
	goto	NoteOn		; call & return
	goto	NoteOff		; 9x nn 00 call & return

;;;;;;;;;;;;;;;;;;;;;;;;;;
;;	channel msg table	;;
;;;;;;;;;;;;;;;;;;;;;;;;;;					
	org		($-1)/.8*.8+.8
MidiInRender_Vec:
	goto	NoteOff			;0x8n	NoteOff
	goto	MidiInRender_NoteOnP	;0x9n	NoteOn
	return				;0xan	Poly After Touch
	goto	MidiInRender_CC		;0xbn	ControlChange
	return				;0xcn	ProgramChange
	return				;0xdn	Channel After Touch
	return				;x0en	Pitch Bend
	return				;0xfn	Sys

;
;
;======================
;	Sound Manager Layer
;======================
;
;-------/ noteconv /--------
;
;	ng ではcurnoteのmsbが1になる
;
noteconv:
;
;　オクターブ重複モードに切り替え
;
	btfsc	basenote,7
	goto	noteconv2
;
	bsf	curnote,7
;
	movf	basenote,w
	subwf	MidiIn_b,w
	btfss	STATUS,C
	return				; ignore lower note
;
	movwf	curnote
	sublw	d'11'
	btfss	STATUS,C
	bsf	curnote,7	 	; ignore higher note
;
	return
;
;--------/ noteconv2 /---------
;
;	全オクターブ反応版
;
noteconv2:
;
	movlw	d'60'
	subwf	MidiIn_b,w
	btfss	STATUS,C
	movf	MidiIn_b,w
;
	movwf	curnote
	movlw	d'12'
	subwf	curnote,f
	btfss	STATUS,C
	goto	noteconv2_skip
	subwf	curnote,f
	btfss	STATUS,C
	goto	noteconv2_skip
	subwf	curnote,f
	btfss	STATUS,C
	goto	noteconv2_skip
	subwf	curnote,f
	btfss	STATUS,C
	goto	noteconv2_skip
	subwf	curnote,f
	btfss	STATUS,C
	goto	noteconv2_skip
	subwf	curnote,f
noteconv2_skip:
	addwf	curnote,f
	return
;
;-------/ NoteOn /--------
;
;	Logical NoteOn
;
;	in:	MidiIn_a - c
;
NoteOn:
	call	noteconv
	btfsc	curnote,7
	return
;
	movlw	high NoteOn_table
	movwf	PCLATH
	rlf	curnote,f
	rlf	curnote,w
	andlw	b'11111100'
	addlw	low NoteOn_table
	movwf	PCL
;
	org		($-1)/.64*.64+.64
NoteOn_table:	
	bsf	porta_back,PORTA_LED_C
	movf	porta_back,w
	movwf	PORTA
	return
	bsf	porta_back,PORTA_LED_CIS
	movf	porta_back,w
	movwf	PORTA
	return
	bsf	porta_back,PORTA_LED_D
	movf	porta_back,w
	movwf	PORTA
	return
	bsf	porta_back,PORTA_LED_DIS
	movf	porta_back,w
	movwf	PORTA
	return
	bsf	portb_back,PORTB_LED_E
	movf	portb_back,w
	movwf	PORTB
	return
	bsf	portb_back,PORTB_LED_F
	movf	portb_back,w
	movwf	PORTB
	return
	bsf	portb_back,PORTB_LED_FIS
	movf	portb_back,w
	movwf	PORTB
	return
	bsf	portb_back,PORTB_LED_G
	movf	portb_back,w
	movwf	PORTB
	return
	bsf	portb_back,PORTB_LED_GIS
	movf	portb_back,w
	movwf	PORTB
	return
	bsf	portb_back,PORTB_LED_A
	movf	portb_back,w
	movwf	PORTB
	return
	bsf	porta_back,PORTA_LED_AIS
	movf	porta_back,w
	movwf	PORTA
	return
	bsf	porta_back,PORTA_LED_B
	movf	porta_back,w
	movwf	PORTA
	return
;

;------/ NoteOff /-------
;
;	Logical NoteOff
;
;	notuse velocity
;
;	in:	MidiIn_a - c
;
NoteOff:
;
	call	noteconv
	btfsc	curnote,7
	return
;
	movlw	high NoteOff_table
	movwf	PCLATH
	rlf	curnote,f
	rlf	curnote,w
	andlw	b'11111100'
	addlw	low NoteOff_table
	movwf	PCL
;
	org		($-1)/.64*.64+.64
NoteOff_table:	
	bcf	porta_back,PORTA_LED_C
	movf	porta_back,w
	movwf	PORTA
	return
	bcf	porta_back,PORTA_LED_CIS
	movf	porta_back,w
	movwf	PORTA
	return
	bcf	porta_back,PORTA_LED_D
	movf	porta_back,w
	movwf	PORTA
	return
	bcf	porta_back,PORTA_LED_DIS
	movf	porta_back,w
	movwf	PORTA
	return
	bcf	portb_back,PORTB_LED_E
	movf	portb_back,w
	movwf	PORTB
	return
	bcf	portb_back,PORTB_LED_F
	movf	portb_back,w
	movwf	PORTB
	return
	bcf	portb_back,PORTB_LED_FIS
	movf	portb_back,w
	movwf	PORTB
	return
	bcf	portb_back,PORTB_LED_G
	movf	portb_back,w
	movwf	PORTB
	return
	bcf	portb_back,PORTB_LED_GIS
	movf	portb_back,w
	movwf	PORTB
	return
	bcf	portb_back,PORTB_LED_A
	movf	portb_back,w
	movwf	PORTB
	return
	bcf	porta_back,PORTA_LED_AIS
	movf	porta_back,w
	movwf	PORTA
	return
	bcf	porta_back,PORTA_LED_B
	movf	porta_back,w
	movwf	PORTA
	return


;------/ AllNoteOff /------
;
; 発音中の音はすべて停止
;	当面一音しかない
; コントローラーもリセットしておく。
;
AllNoteOff:
	movlw	~PORTA_LED_ALL
	andwf	porta_back,f
	movf	porta_back,w
	movwf	PORTA
;
	movlw	~PORTB_LED_ALL
	andwf	portb_back,f
	movf	portb_back,w
	movwf	PORTB
	return
;
;
;-------/ ResetAllCC /------
;
;	benderなどすべてを中央地に戻す
;
ResetAllCC:
	return
;
;

;
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; EEPROM ACCESS
;;
;-------/ ReadRom /--------
;
;	in:	w romadr
;	out	w value
ReadRom
	bsf	STATUS,RP0
	movwf	EEADR
	bsf	EECON1,RD
	movf	EEDATA,w
	bcf	STATUS,RP0
	return
;
;=========================
;	EEPROM SECTION
;=========================
;
;	256 bytes
;	action data
;
;
	org		0x2100
	de		0x00		; midiIn Channel Number(0 : ch 1 ...)
	de		d'60'		; basenote

	end
