;------------------------------------------------------------------------------
; Dumpstar
;------------------------------------------------------------------------------
; バージョン履歴
; 2024-03-26 Ver.0.06	TMR0割り込み周期で4,882Hzの微かな音が混ざるため、プリスケーラを半分の1:2とした。
; 2020-02-20 Ver.0.05	SDと同時にLT,HT,CLをトリガするSDWITHを追加した。
; 2020-01-09 Ver.0.04	CYCUTをCYのノートオフでもできるようにした。
; 2016-05-14 Ver.0.03	CYCUTのベロシティを偶数奇数のどちらでも設定できるようにした。
; 2016-05-08 Ver.0.02	完成
; 2016-05-07 Ver.0.01	トリガオフを実装
; 2016-05-07 Ver.0.00	とりあえず音を鳴らすプログラムを作っている途中にUSARTのTXを出力ピンに割り当てていることが判明。
;			音源トリガのパルス幅は1msで問題ないことが分かったが、念のため2msにすることにした。
; 2016-05-05		基板完成
;------------------------------------------------------------------------------
; IRP,RP1,RP0を切り替える所で◆ RP=, ● IRP=をコメントに置く
; PCLATHを切り替える所で■ PCLATHをコメントに置く
; サブルーチンからの戻りの時、
;  RP1,RP0は0に戻してリターンする。
;  IRP,PCLATHは戻さずにリターンする。
; ルーチン呼び出しの時もRP=0を基本として記述している。
;------------------------------------------------------------------------------
; デバイス	PIC16F88
; 動作周波数	20MHz
;------------------------------------------------------------------------------
	LIST		P=PIC16F88,R=DEC,N=0
	INCLUDE		"P16F88.INC"
	ERRORLEVEL	0,-302,-306,-307
	__CONFIG _CONFIG1, 0x3F42
				; FOSC : HS
				; WDTE : Disable
				; PWRTE: Enable
				; MCLRE: RA5=IO
				; BODEN: Enable
				; LVP  : RB3=IO
				; CPD  : Not Protect
				; WRT  : Protection off
				; DEBUG: Disable
				; CCPMX: CCP1=RB0
				; CP   : Not Protect
	__CONFIG _CONFIG2, 0x3FFC
				; FCMEN: Disable
				; IESO : Disable
;------------------------------------------------------------------------------
; 変数宣言
;------------------------------------------------------------------------------
MIDI		EQU	0x20		; 受信データ
EVENT		EQU	0x21		; 最後のイベントNo
PARAM1		EQU	0x22		; EVENTに続くパラメータ
PARAM2		EQU	0x23		; PARAM1に続くパラメータ
CHANNEL		EQU	0x24		; 最後のMIDIイベントから分離したチャンネル情報
EX_ADDR1	EQU	0x25		; SysEX用アドレス(PARAM1)自動インクリメントなし
EX_ADDR2	EQU	0x26		; SysEX用アドレス(PARAM2)自動インクリメント対象、SYSEX_RESET用のメーカー判別チェック状態
EX_DATA		EQU	0x27		; 前回のSysExデータ

SDWITH		EQU	0x28		; bit0:LT bit1:HT bit2:CL
CYCUT		EQU	0x29		; 0=カットなし 1=ODD VEL 2=EVEN VEL 3=強制
;空き		EQU	0x2A
;空き		EQU	0x2B
PORTA_		EQU	0x2C		; PORTA出力用
PORTB_		EQU	0x2D		; PORTB出力用
FLAG1		EQU	0x2E		; システムリセットで影響を受けるフラグ
PFLAG		EQU	0x2F		; システムリセットがかかってもリセットされないフラグ

;空き		EQU	0x30;-0x4F
RX_CHANNEL	EQU	0x50;-0x5F	; 受信チャンネルフラグ

; FIFOバッファと処理用変数
READPTR1	EQU	0x60		; FIFOリードポインタ
WRITEPTR1	EQU	0x61		; FIFOライトポインタ
USEDSIZE1	EQU	0x62		; FIFO使用サイズ
READDATA	EQU	0x63		; 読み込みデータ作業領域
WRITEDATA1	EQU	0x64		; 書き込みデータ作業領域、割り込みで上書きされるので個別に用意する
FIFOSTATUS	EQU	0x65		; FIFOステータスフラグ
FIFOBUF1	EQU	0x66		; MIDIバッファ(ここを変更すると、アセンブル時に自動的にバッファのサイズが計算される)
FIFOEND		EQU	0x70

; 一時使用変数
TMP00		EQU	0x70
TMP01		EQU	0x71
TMP02		EQU	0x72
TMP03		EQU	0x73
TMP10		EQU	0x74
TMP11		EQU	0x75
TMP12		EQU	0x76
TMP13		EQU	0x77
TMP20		EQU	0x78
TMP21		EQU	0x79
TMP22		EQU	0x7A
TMP23		EQU	0x7B

W_		EQU	0x7C		; 割り込みの退避用
STATUS_		EQU	0x7D
PCLATH_		EQU	0x7E
FSR_		EQU	0x7F

;空き		EQU	0x0A0;-0x0A7
OFF_BD		EQU	0x0A8		; トリガーオフカウンタ 8バイト
OFF_SD		EQU	0x0A9
OFF_LT		EQU	0x0AA
OFF_HT		EQU	0x0AB
OFF_CL		EQU	0x0AC
OFF_CY		EQU	0x0AD
OFF_HH		EQU	0x0AE
OFF_CUT		EQU	0x0AF
KEYASSIGN	EQU	0x0B0;-0x0EF	; キーアサイン表 64バイト
;------------------------------------------------------------------------------
; 定数宣言
;------------------------------------------------------------------------------
EX_PHASE1	EQU	0		; FLAG1-0 エクスクルーシブフェーズ
EX_PHASE2	EQU	1		; FLAG1-1 エクスクルーシブフェーズ
EX_PHASE4	EQU	2		; FLAG1-2 エクスクルーシブフェーズ
VOLUME0		EQU	3		; FLAG1-3 ボリュームがゼロの時1
EXPRESSION0	EQU	4		; FLAG1-4 エクスプレッションがゼロの時1

PHASE_ID	EQU	0		; FLAG1-[210] ID受信フェーズ
PHASE_ADDR	EQU	1		; FLAG1-[210] アドレス受信フェーズ
PHASE_DATA	EQU	2		; FLAG1-[210] データ受信フェーズ
PHASE_IGNORE	EQU	3		; FLAG1-[210] 無視フェーズ
PHASE_RESET	EQU	4		; FLAG1-[210] メーカー別リセット検出モード

VENDOR_RESET	EQU	0		; PFLAG-0 各社リセットを受ける場合に1

EVENT_RCVD	EQU	7		; EVENT-7 EVENTを受信した 1=受信済み
PARAM_NOTRCVD	EQU	7		; PARAMx-7 PARAMxを受信していない 0=受信済み
FIFO1EMPTY	EQU	0		; FIFOSTATUS-0 FIFO1読み出し時、データが無かった
FIFO1FULL	EQU	1		; FIFOSTATUS-1 FIFO1書き込み時、バッファが一杯だった
FIFO1SIZE	EQU	FIFOEND-FIFOBUF1 ; FIFOバッファのサイズ

NONE		EQU	0		; インスト番号
BD		EQU	1
SD		EQU	2
LT		EQU	3
HT		EQU	4
CL		EQU	5
CY		EQU	6
HH		EQU	7
CUT		EQU	8
PULSEWIDTH	EQU	40		; トリガパルス幅(タイマ割り込みの回数をセット)
;------------------------------------------------------------------------------
; 電源ON
;------------------------------------------------------------------------------
	ORG	0
	GOTO	POWER_ON
;------------------------------------------------------------------------------
; 割り込み分岐
; 割り込み処理は次のタイマー割り込みが来るまでの204.8us(1024ステップ)以内に完了すること。
; MIDIの受信割り込みは最短で320us(1600ステップ)ごとに発生する。
;------------------------------------------------------------------------------
	ORG	4			; 割り込みベクタ
	MOVWF	W_			; Wセーブ
	SWAPF	STATUS,W		; STATUSセーブ
	MOVWF	STATUS_
	MOVF	PCLATH,W		; PCLATHセーブ
	MOVWF	PCLATH_
	MOVF	FSR,W			; FSRセーブ
	MOVWF	FSR_

	BCF	STATUS,RP0		; ◆ RP=0
	BCF	STATUS,RP1
INT_TMR0				; タイマー割り込み
	BTFSS	INTCON,TMR0IF
	 GOTO	INT_USART
	BCF	INTCON,TMR0IF		; フラグリセット
	CALL	trigger_off

INT_USART				; USART受信割り込み
	BTFSS	PIR1,RCIF
	 GOTO	INTRETURN
	BTFSS	FIFOSTATUS,FIFO1FULL	; 前回バッファがあふれていない場合は、そのままバッファに入れる
	 GOTO	MIDIQUEUE
	MOVLW	0x80			; 前回バッファあふれた場合は不正な演奏になるため、
	SUBWF	RCREG,W			; 0x80以上が来るまで0x00-0x7Fをバッファに入れない
	BTFSS	STATUS,C
	 GOTO	INTRETURN
	MOVF	RCREG,W			; 0xF8以上のリアルタイムメッセージも0x80以上のイベントと関係なく
	SUBLW	0xF7			; 出現するため、FULLフラグが解除されないように受信しないでおく。
	BTFSS	STATUS,C
	 GOTO	INTRETURN
MIDIQUEUE
	MOVF	RCREG,W			; 受信データを取り出してFIFOに書き込み
	CALL	fifo1write		; FIFOがあふれたら次に受信するタイミングで対処する。

INTRETURN
	MOVF	FSR_,W			; FSRリストア
	MOVWF	FSR
	MOVF	PCLATH_,W		; PCLATHリストア
	MOVWF	PCLATH
	SWAPF	STATUS_,W		; STATUSリストア
	MOVWF	STATUS
	SWAPF	W_,F			; Wリストア
	SWAPF	W_,W
	RETFIE
;------------------------------------------------------------------------------
; 電源投入時のSFR初期化
;------------------------------------------------------------------------------
POWER_ON				; 電源ON I/O初期化 未使用ポートは0を出力
	CLRF	PORTA
	CLRF	PORTB

	BSF	STATUS,RP0		; ◆ RP=1
	CLRF	ANSEL			; PORTAはデジタルI/O
	CLRF	TRISA			; [4]CY-MUTE [0]BD
	MOVLW	B'00100100'		; [7]HT [6]LT [5]USART [4]SD [3]HH [2]USART [1]CY [0]CL
	MOVWF	TRISB

	MOVLW	B'11010000'		; TMR0 内部クロック プリスケーラ=TMR0 1:4(204.8usで割り込み発生)
	MOVWF	OPTION_REG

	CLRF	TXSTA			; USART初期化
	MOVLW	9			; 20,000,000/(31,250*64)-1=9
	MOVWF	SPBRG
	BSF	PIE1,RCIE		; USART受信割り込み許可
	BCF	STATUS,RP0		; ◆ RP=0
	BSF	RCSTA,SPEN		; USART初期化 まだ受信は開始しない

	MOVLW	B'11100000'		; 割り込み許可(GIE,PEIE,TMR0IE)
	MOVWF	INTCON
;------------------------------------------------------------------------------
; ループ前処理 システムリセット実行
;------------------------------------------------------------------------------
	CLRF	PFLAG
	CLRF	PARAM1
	CALL	system_reset
;------------------------------------------------------------------------------
; メインループ
;------------------------------------------------------------------------------
MAINLOOP
	CALL	fifo1read		; MIDI受信バッファチェック
	BTFSC	FIFOSTATUS,FIFO1EMPTY
	 GOTO	MAINLOOP		; バッファが空の時

EVENTCHECK				; 受信データの種類判定
	MOVWF	MIDI			; FIFOから読み出したデータを保存

	MOVF	MIDI,W
	SUBLW	0x7F			; 0x00〜0x7Fの場合パラメータ解析へ
	BTFSC	STATUS,C
	 GOTO	proc_param
	
	MOVF	MIDI,W
	SUBLW	0xF7			; 0x80〜0xF7の場合イベント解析へ
	BTFSC	STATUS,C
	 GOTO	proc_event

	MOVF	MIDI,W
	SUBLW	0xF8			; 0xF8の場合クロック
	BTFSC	STATUS,Z
	 GOTO	event_clock
	
	MOVF	MIDI,W
	SUBLW	0xFA			; 0xFAの場合スタート
	BTFSC	STATUS,Z
	 GOTO	event_start

	MOVF	MIDI,W
	SUBLW	0xFB			; 0xFBの場合コンティニュー
	BTFSC	STATUS,Z
	 GOTO	event_continue
	
	MOVF	MIDI,W
	SUBLW	0xFC			; 0xFCの場合ストップ
	BTFSC	STATUS,Z
	 GOTO	event_stop
	
	MOVF	MIDI,W
	SUBLW	0xFE			; 0xFEの場合アクティブセンシング
	BTFSC	STATUS,Z
	 GOTO	event_active

	MOVF	MIDI,W
	SUBLW	0xFF			; 0xFFの場合システムリセット
	BTFSC	STATUS,Z
	 GOTO	event_reset
EVENT_END				; その他(0xF9,0xFD)は定義されていないので無視
	GOTO	MAINLOOP
;------------------------------------------------------------------------------
; メインループ終了
;------------------------------------------------------------------------------
;------------------------------------------------------------------------------
; イベント解析 80〜F7の時にここへ来る
;------------------------------------------------------------------------------
proc_event
	MOVLW	B'11111000'		; エクスクルーシブフェーズをPHASE_IDにセット
	ANDWF	FLAG1,F

	MOVLW	0xF1			; ランニングステータスはチャンネルメッセージに適用されるため
	SUBWF	MIDI,W			; 0xF1〜0xF7は無効なイベントに書き換える
	BTFSC	STATUS,C
	 CLRF	MIDI
	MOVF	MIDI,W			; 今回のイベントを記録
	MOVWF	EVENT
	ANDLW	0x0F			; チャンネル情報だけを取り出してあらかじめ入れておく
	MOVWF	CHANNEL

	BSF	PARAM1,PARAM_NOTRCVD	; PARAM1を無効にする
	GOTO	EVENT_END		; イベント解析終了
;------------------------------------------------------------------------------
; パラメータ解析 00〜7F、各イベント処理へ分岐
;------------------------------------------------------------------------------
proc_param
	BTFSS	EVENT,EVENT_RCVD	; イベントが一度も受信されていない場合は何のパラメータか不明のため戻る
	 GOTO	EVENT_END
	BTFSS	PARAM1,PARAM_NOTRCVD	; PARAM1の受信か？
	 GOTO	PROC_PARAM2
PROC_PARAM1
	MOVF	MIDI,W			; PARAM1へ代入
	MOVWF	PARAM1

	MOVF	EVENT,W
	SUBLW	0xBF			; 前回のイベントが0x80〜0xBFか？
	BTFSC	STATUS,C
	 GOTO	EVENT_END		; 0x80〜0xBFはPARAM2が必要なのでリターン

	MOVF	EVENT,W
	SUBLW	0xCF			; 前回のイベントが0xC0〜0xCFか？
	BTFSC	STATUS,C
	 GOTO	event_program		; プログラムチェンジ

	MOVF	EVENT,W
	SUBLW	0xDF			; 前回のイベントが0xD0〜0xDFか？
	BTFSC	STATUS,C
	 GOTO	event_chpress		; チャンネルプレッシャ

	BTFSC	FLAG1,EX_PHASE2		; 0xF0でエクスクルーシブフェーズがPHASE_DATA,PHASE_IGNORE,PHASE_RESETの場合は、
	 GOTO	event_sysex		; データを1バイトづつ受信する。PHASE_RESETはEX_PHASE4フラグのみONとする
	BTFSC	FLAG1,EX_PHASE4
	 GOTO	event_sysex

	GOTO	EVENT_END		; 0xE0〜0xEF、0xF0(PHASE_ID,PHASE_ADDR)はPARAM2が必要なのでリターン
					; 0xF1以上はここへは来ない
PROC_PARAM2
	MOVF	MIDI,W			; PARAM2へ代入
	MOVWF	PARAM2

	MOVF	EVENT,W
	SUBLW	0x8F			; 前回のイベントが0x80〜0x8Fか？
	BTFSC	STATUS,C
	 GOTO	event_noteoff		; ノートオフ

	MOVF	EVENT,W
	SUBLW	0x9F			; 前回のイベントが0x90〜0x9Fか？
	BTFSC	STATUS,C
	 GOTO	event_noteon		; ノートオン

	MOVF	EVENT,W
	SUBLW	0xAF			; 前回のイベントが0xA0〜0xAFか？
	BTFSC	STATUS,C
	 GOTO	event_polypress		; ポリフォニックキープレッシャ

	MOVF	EVENT,W
	SUBLW	0xBF			; 前回のイベントが0xB0〜0xBFか？
	BTFSC	STATUS,C
	 GOTO	event_control		; コントロールチェンジ
					; 0xC0〜0xDFはここへは来ない
	MOVF	EVENT,W
	SUBLW	0xEF			; 前回のイベントが0xE0〜0xEFか？
	BTFSC	STATUS,C
	 GOTO	event_pitch		; ピッチベンド

	GOTO	event_sysex		; 0xF0でエクスクルーシブフェーズがPHASE_ID,PHASE_ADDRの場合は
					; データを2バイトづつ受信する
					; 0xF1以上はここへは来ない
;------------------------------------------------------------------------------
; 80 ノートオフ
;  ノートがCYでCYカットが4の時にカット信号を出力する
;------------------------------------------------------------------------------
event_noteoff
	MOVF	CHANNEL,W		; 受信チャンネル確認
	ADDLW	RX_CHANNEL
	MOVWF	FSR
	MOVF	INDF,W
	BTFSC	STATUS,Z
	 GOTO	NOTEOFF_RETURN

	BCF	STATUS,IRP		; ● IRP=0
	RRF	PARAM1,W		; ノート番号からインスト番号への変換
	ANDLW	0x7F
	ADDLW	KEYASSIGN
	MOVWF	FSR			; 表アドレスセット

	MOVF	INDF,W
	BTFSC	PARAM1,0		; 奇数アドレスの時は上位4ビットのデータを使う
	 SWAPF	INDF,W
	ANDLW	7
	MOVWF	TMP00

	SUBLW	CY
	BTFSS	STATUS,Z		; 割り当てインストがCY以外の時は実行を中断
	 GOTO	NOTEOFF_RETURN

	MOVLW	4			; CYCUT=4(CY NoteOff)?
	SUBWF	CYCUT,W
	BTFSS	STATUS,Z
	 GOTO	NOTEOFF_RETURN		;  カットしない

	MOVLW	CUT			;  カットする
	CALL	out_bit
	IORWF	PORTA_,F
	MOVLW	PULSEWIDTH
	BSF	STATUS,RP0		; ◆ RP=1
	MOVWF	OFF_CUT
	BCF	STATUS,RP0		; ◆ RP=0
	MOVF	PORTA_,W
	MOVWF	PORTA
NOTEOFF_RETURN
	BSF	PARAM1,PARAM_NOTRCVD	; PARAM1を無効にする
	GOTO	EVENT_END
;------------------------------------------------------------------------------
; 90 ノートオン
; 使用変数
;   TMP00,01,02,03,TMP23(out_port,out_bit)
;------------------------------------------------------------------------------
event_noteon
	MOVF	PARAM2,W		; PARAM2(ベロシティ)が0の時は、ノートオフで処理する
	BTFSC	STATUS,Z
	 GOTO	event_noteoff
	
	MOVF	CHANNEL,W		; 受信チャンネル確認
	ADDLW	RX_CHANNEL
	MOVWF	FSR
	MOVF	INDF,W
	BTFSC	STATUS,Z
	 GOTO	NOTEON_RETURN

	RLF	FLAG1,W			; ボリュームとエクスプレッションのどちらかが0なら、ミュートなので実行しない。
	IORWF	FLAG1,W
	MOVWF	TMP00
	BTFSC	TMP00,EXPRESSION0
	 GOTO	NOTEON_RETURN

	BCF	STATUS,IRP		; ● IRP=0
	RRF	PARAM1,W		; ノート番号からインスト番号への変換
	ANDLW	0x7F
	ADDLW	KEYASSIGN
	MOVWF	FSR			; 表アドレスセット

	MOVF	INDF,W
	BTFSC	PARAM1,0		; 奇数アドレスの時は上位4ビットのデータを使う
	 SWAPF	INDF,W
	ANDLW	7
	MOVWF	TMP00

	BTFSC	STATUS,Z		; 割り当てインストが0の時は未割当として実行を中断
	 GOTO	NOTEON_RETURN
	CALL	out_port		; インストのビットをONにする
	MOVWF	FSR
	MOVF	TMP00,W
	CALL	out_bit
	IORWF	INDF,F

	MOVF	TMP00,W			; SDと同時に鳴らすインストの判定
	SUBLW	SD
	BTFSS	STATUS,Z
	 GOTO	SDNOTWITH
	RRF	SDWITH,W		; SDと同時にONするインストのフラグ
	MOVWF	TMP01
	MOVLW	LT
	MOVWF	TMP02			; SDと同時にONするインスト番号3-5
	MOVLW	3
	MOVWF	TMP03			; DO-LOOP回数カウント
SDWITH_LOOP
	BTFSS	STATUS,C
	 GOTO	SDWITH_NEXT
	MOVF	TMP02,W
	CALL	out_port		; インストのビットをONにする
	MOVWF	FSR
	MOVF	TMP02,W
	CALL	out_bit
	IORWF	INDF,F

	MOVF	TMP02,W			; 同時に鳴らすインストのパルス幅カウンタをセット
	ADDLW	OFF_BD-1
	MOVWF	FSR
	MOVLW	PULSEWIDTH
	MOVWF	INDF
SDWITH_NEXT
	INCF	TMP02,F
	RRF	TMP01,F			; シフトしたSDWITH
	DECFSZ	TMP03,F
	 GOTO	SDWITH_LOOP
SDNOTWITH
	MOVF	TMP00,W			; パルス幅カウンタをセット
	ADDLW	OFF_BD-1
	MOVWF	FSR
	MOVLW	PULSEWIDTH
	MOVWF	INDF

	MOVF	TMP00,W			; HHでカットがONの時は、カットのビットもONにする
	SUBLW	HH
	BTFSS	STATUS,Z
	 GOTO	NOTEON_GO
	MOVF	CYCUT,W			; CYCUT=0 or 4(OFF or CY NOTEOFF)?
	ANDLW	3
	BTFSC	STATUS,Z		;  カットしない
	 GOTO	NOTEON_GO
	SUBLW	3			; CYCUT=3(ForceON)?
	BTFSC	STATUS,Z		;  カットする
	 GOTO	NOTEON_CYCUT
	MOVF	PARAM2,W		; ベロシティのbit0とCYCUTのbit0が一致しているか?
	XORWF	CYCUT,W
	ANDLW	1
	BTFSS	STATUS,Z
	 GOTO	NOTEON_GO		;  カットしない
NOTEON_CYCUT
	MOVLW	CUT
	CALL	out_bit
	IORWF	PORTA_,F
	MOVLW	PULSEWIDTH
	BSF	STATUS,RP0		; ◆ RP=1
	MOVWF	OFF_CUT
	BCF	STATUS,RP0		; ◆ RP=0
NOTEON_GO
	MOVF	PORTA_,W
	MOVWF	PORTA
	MOVF	PORTB_,W
	MOVWF	PORTB
NOTEON_RETURN
	BSF	PARAM1,PARAM_NOTRCVD	; PARAM1を無効にする
	GOTO	EVENT_END
;------------------------------------------------------------------------------
; A0 ポリフォニックキープレッシャ 実装しない
;------------------------------------------------------------------------------
event_polypress
	BSF	PARAM1,PARAM_NOTRCVD	; PARAM1を無効にする
	GOTO	EVENT_END
;------------------------------------------------------------------------------
; B0 コントロールチェンジ
; 使用変数
;   TMP00
;------------------------------------------------------------------------------
event_control
	MOVF	CHANNEL,W		; 受信チャンネル確認
	ADDLW	RX_CHANNEL
	MOVWF	FSR
	MOVF	INDF,W
	BTFSC	STATUS,Z
	 GOTO	CONTROL_RETURN

CONTROL_SUSTAIN				; サスティン=OFFの時カットパルスを送出する
	MOVLW	64
	SUBWF	PARAM1,W
	BTFSS	STATUS,Z
	 GOTO	CONTROL_VOLUME
	MOVF	PARAM2,W
	SUBLW	63
	BTFSS	STATUS,C
	 GOTO	CONTROL_RETURN
	GOTO	NOTEON_CYCUT
CONTROL_VOLUME
	MOVLW	7
	SUBWF	PARAM1,W
	BTFSS	STATUS,Z
	 GOTO	CONTROL_EXPRESSION
	MOVLW	B'00001000'		; VOLUME0のビット位置に対応する
	MOVWF	TMP00
	GOTO	CONTROL_ZERO
CONTROL_EXPRESSION
	MOVLW	11
	SUBWF	PARAM1,W
	BTFSS	STATUS,Z
	 GOTO	CONTROL_RETURN
	MOVLW	B'00010000'		; EXPRESSION0のビット位置に対応する
	MOVWF	TMP00
CONTROL_ZERO
	MOVF	PARAM2,W
	BTFSS	STATUS,Z
	 GOTO	CONTROL_ZERO_FALSE
CONTROL_ZERO_TRUE			; ゼロのフラグを立てる
	MOVF	TMP00,W
	IORWF	FLAG1,F
	GOTO	CONTROL_RETURN
CONTROL_ZERO_FALSE			; ゼロのフラグを落とす
	COMF	TMP00,W
	ANDWF	FLAG1,F
CONTROL_RETURN
	BSF	PARAM1,PARAM_NOTRCVD	; PARAM1を無効にする
	GOTO	EVENT_END
;------------------------------------------------------------------------------
; C0 プログラムチェンジ
; 使用変数
;   TMP00,TMP22(load_keyassign)
;------------------------------------------------------------------------------
event_program
	MOVF	CHANNEL,W		; 受信チャンネル確認
	ADDLW	RX_CHANNEL
	MOVWF	FSR
	MOVF	INDF,W
	BTFSC	STATUS,Z
	 GOTO	PROGRAM_RETURN

	MOVLW	124			; P#124未満は実行しない
	SUBWF	PARAM1,W
	BTFSS	STATUS,C
	 GOTO	PROGRAM_RETURN
	CALL	load_keyassign
PROGRAM_RETURN
	BSF	PARAM1,PARAM_NOTRCVD	; PARAM1を無効にする
	GOTO	EVENT_END
;------------------------------------------------------------------------------
; D0 チャンネルプレッシャ 実装しない
;------------------------------------------------------------------------------
event_chpress
	BSF	PARAM1,PARAM_NOTRCVD	; PARAM1を無効にする
	GOTO	EVENT_END
;------------------------------------------------------------------------------
; E0 ピッチベンド 実装しない
;------------------------------------------------------------------------------
event_pitch
	BSF	PARAM1,PARAM_NOTRCVD	; PARAM1を無効にする
	GOTO	EVENT_END
;------------------------------------------------------------------------------
; F0 エクスクルーシブ
; 1度目のコール PARAM1にメーカーID、PARAM2に機種IDが入る(PHASE_ID)
; 2度目のコール PARAM1に上位アドレス、PARAM2に下位アドレスが入る(PHASE_ADDR)
; 3度目以降のコール PARAM1にデータが入る(PHASE_DATA) 2バイト必要な場合は、SYSEX_DATABACKUPで一度目をリターンさせる
; SYSEXの個々の処理はパート2に記述する
;------------------------------------------------------------------------------
event_sysex
	BTFSC	FLAG1,EX_PHASE4		; PHASE_RESET?
	 GOTO	SYSEX_RESET

	MOVF	FLAG1,W			; PHASE_IGNORE?
	ANDLW	B'00000011'
	XORLW	PHASE_IGNORE
	BTFSC	STATUS,Z
	 GOTO	SYSEX_RETURN

	MOVF	FLAG1,W			; PHASE_DATA?
	ANDLW	B'00000011'
	XORLW	PHASE_DATA
	BTFSC	STATUS,Z
	 GOTO	SYSEX_DATASET
	
	MOVF	FLAG1,W			; PHASE_ADDR?
	ANDLW	B'00000011'
	XORLW	PHASE_ADDR
	BTFSC	STATUS,Z
	 GOTO	SYSEX_ADDRESS
SYSEX_IDCHECK				; PHASE_ID
	MOVLW	0x7D			; メーカーIDチェック
	SUBWF	PARAM1,W
	BTFSS	STATUS,Z
	 GOTO	SYSEX_RESETCHECK	; ID違い、他メーカーチェック
	MOVLW	0x00			; 機種IDチェック
	SUBWF	PARAM2,W
	BTFSS	STATUS,Z
	 GOTO	SYSEX_UNMATCH		; ID違い、フェーズ3へ移行

	MOVF	FLAG1,W
	ANDLW	B'11111100'
	ADDLW	PHASE_ADDR		; IDチェック完了、PHASE_ADDRへ移行
	MOVWF	FLAG1
	GOTO	SYSEX_RETURN
SYSEX_RESETCHECK
	BTFSS	PFLAG,VENDOR_RESET	; 各社リセットを受けない場合は、ID違い、フェーズ3へ移行
	 GOTO	SYSEX_UNMATCH

	CLRF	EX_ADDR2		; チェックアドレスリセット

	MOVLW	0x41			; メーカーIDチェック
	SUBWF	PARAM1,W
	BTFSS	STATUS,Z
	 GOTO	SYSEX_RESETCHECK_YAMAHA	; ID違い、他メーカーチェック
	MOVLW	0x10			; 機種IDチェック
	SUBWF	PARAM2,W
	BTFSS	STATUS,Z
	 GOTO	SYSEX_UNMATCH		; ID違い、フェーズ3へ移行

	MOVLW	0			; Roland系列
	GOTO	SYSEX_RESETCHECK_OK

SYSEX_RESETCHECK_YAMAHA
	MOVLW	0x43			; メーカーIDチェック
	SUBWF	PARAM1,W
	BTFSS	STATUS,Z
	 GOTO	SYSEX_RESETCHECK_GM	; ID違い、他メーカーチェック
	MOVLW	0x10			; 機種IDチェック
	SUBWF	PARAM2,W
	BTFSS	STATUS,Z
	 GOTO	SYSEX_UNMATCH		; ID違い、フェーズ3へ移行

	MOVLW	12			; Yamaha系列
	GOTO	SYSEX_RESETCHECK_OK

SYSEX_RESETCHECK_GM
	MOVLW	0x7E			; メーカーIDチェック
	SUBWF	PARAM1,W
	BTFSS	STATUS,Z
	 GOTO	SYSEX_UNMATCH		; ID違い、フェーズ3へ移行
	MOVLW	0x7F			; 機種IDチェック
	SUBWF	PARAM2,W
	BTFSS	STATUS,Z
	 GOTO	SYSEX_UNMATCH		; ID違い、フェーズ3へ移行

	MOVLW	17			; GM
SYSEX_RESETCHECK_OK
	MOVWF	EX_ADDR2
	BSF	FLAG1,EX_PHASE4		; 機種が入った場合は、フェーズ4へ移行
	GOTO	SYSEX_RETURN

SYSEX_ADDRESS				; PHASE_ADDR
	MOVF	PARAM1,W		; レジスタアドレスをセット
	MOVWF	EX_ADDR1
	MOVF	PARAM2,W
	MOVWF	EX_ADDR2
	BSF	EX_DATA,PARAM_NOTRCVD	; 前回のバックアップデータをクリア

	MOVF	FLAG1,W
	ANDLW	B'11111100'
	ADDLW	PHASE_DATA		; アドレスセット完了、PHASE_DATAへ移行
	MOVWF	FLAG1
	GOTO	SYSEX_RETURN

SYSEX_DATASET				; PHASE_DATA
	GOTO	SYSEX_DATASET_P2

SYSEX_RESET				; PHASE_RESET
	GOTO	SYSEX_RESET_P2

SYSEX_UNMATCH				; PHASE_IGNOREをセットして戻る
	MOVLW	PHASE_IGNORE
	IORWF	FLAG1,F
	GOTO	SYSEX_RETURN
SYSEX_INC_RETURN			; アドレスをインクリメントしてリターン
	INCF	EX_ADDR2,F
	BSF	EX_DATA,PARAM_NOTRCVD	; バックアップしたデータを無効化する
SYSEX_RETURN				; アドレスをインクリメントせずにリターン
	BSF	PARAM1,PARAM_NOTRCVD	; PARAM1を無効にする
	GOTO	EVENT_END

;------------------------------------------------------------------------------
; F8 クロック 実装しない
;------------------------------------------------------------------------------
event_clock
	GOTO	EVENT_END
;------------------------------------------------------------------------------
; FA スタート 実装しない
;------------------------------------------------------------------------------
event_start
	GOTO	EVENT_END
;------------------------------------------------------------------------------
; FB コンティニュー 実装しない
;------------------------------------------------------------------------------
event_continue
	GOTO	EVENT_END
;------------------------------------------------------------------------------
; FC ストップ 実装しない
;------------------------------------------------------------------------------
event_stop
	GOTO	EVENT_END
;------------------------------------------------------------------------------
; FE アクティブセンシング 実装しない
;------------------------------------------------------------------------------
event_active
	GOTO	EVENT_END
;------------------------------------------------------------------------------
; FF システムリセット 電源投入と同じ初期化を行う
;------------------------------------------------------------------------------
event_reset
	; CALL	system_reset
	GOTO	EVENT_END
;------------------------------------------------------------------------------
; システムリセット
; 使用変数
;	TMP22(load_keyassign)
;------------------------------------------------------------------------------
system_reset
	BCF	RCSTA,CREN		; 受信停止
	CLRF	TMR0			; 無駄なタイマー割り込みの発生を保留する
	BSF	STATUS,RP0		; ◆ RP=1
	CLRF	OFF_BD			; 音源OFF
	CLRF	OFF_SD
	CLRF	OFF_LT
	CLRF	OFF_HT
	CLRF	OFF_CL
	CLRF	OFF_CY
	CLRF	OFF_HH
	CLRF	OFF_CUT
	BCF	STATUS,RP0		; ◆ RP=0
	CLRF	PORTA
	CLRF	PORTB
	CLRF	PORTA_
	CLRF	PORTB_

	MOVLW	FIFOBUF1		; FIFO初期化
	MOVWF	READPTR1		; FIFOバッファをエンプティ状態にする
	MOVWF	WRITEPTR1
	CLRF	USEDSIZE1
	CLRF	FIFOSTATUS

	MOVLW	16
	MOVWF	TMP22
	MOVLW	RX_CHANNEL
	MOVWF	FSR
RESET_L1
	CLRF	INDF
	INCF	FSR,F
	DECFSZ	TMP22,F
	 GOTO	RESET_L1
	INCF	RX_CHANNEL+9,F		; 受信チャンネル=10のみ
	MOVLW	1
	MOVWF	CYCUT
	CLRF	SDWITH
	CLRF	EVENT			; イベント処理変数リセット
	CLRF	FLAG1

	MOVLW	3			; デフォルトキーマップを読み込む
	CALL	load_keyassign
	BSF	RCSTA,CREN		; 受信開始
	RETURN
;------------------------------------------------------------------------------
; SYSEX分岐
; F0 7D 00 までチェック済みで次のEX_ADDR1を使って分岐させる。
;------------------------------------------------------------------------------
SYSEX_DATASET_P2
	MOVLW	0x00			; 0x00はシステムリセット
	SUBWF	EX_ADDR1,W
	BTFSC	STATUS,Z
	 GOTO	SYSEX_SYSTEMRESET

	MOVLW	0x01			; 0x01は受信チャンネル
	SUBWF	EX_ADDR1,W
	BTFSC	STATUS,Z
	 GOTO	SYSEX_CHANNEL

	MOVLW	0x02			; 0x02はキーアサイン
	SUBWF	EX_ADDR1,W
	BTFSC	STATUS,Z
	 GOTO	SYSEX_KEYASSIGN

	MOVLW	0x03			; 0x03はCYカット
	SUBWF	EX_ADDR1,W
	BTFSC	STATUS,Z
	 GOTO	SYSEX_CYCUT

	MOVLW	0x04			; 0x04はSDWITH
	SUBWF	EX_ADDR1,W
	BTFSC	STATUS,Z
	 GOTO	SYSEX_SDWITH

	MOVLW	0x10			; 0x10は各社リセット
	SUBWF	EX_ADDR1,W
	BTFSC	STATUS,Z
	 GOTO	SYSEX_VENDORRESET

	MOVLW	0x11			; 0x11はアサインクリア
	SUBWF	EX_ADDR1,W
	BTFSC	STATUS,Z
	 GOTO	SYSEX_CLEARASSIGN

	MOVLW	0x12			; 0x12はアサイン保存
	SUBWF	EX_ADDR1,W
	BTFSC	STATUS,Z
	 GOTO	SYSEX_SAVEASSIGN

	GOTO	SYSEX_UNMATCH		; 未対応アドレスはPHASE_IGNOREへ移行
;------------------------------------------------------------------------------
; SYSEX個別内容
; EX_ADDR1は固定
;
; SYSEX_DATABACKUPでリターンすると、PARAM1をEX_DATAに格納して次のデータを受信する。
;   EX_ADDR2をインクリメントしないので、2バイト用・ニブルデータ等に使用できる。
; SYSEX_INC_RETURNでリターンすると、EX_ADDR2をインクリメントする。
;   
; SYSEX_UNMATCHでリターンさせると以降のエクスクルーシブは無条件に無視される。
;   固定長エクスクルーシブの終端に使う。
;
;------------------------------------------------------------------------------
SYSEX_SYSTEMRESET			; F0 7D 00 00 [00] [PARAM1] F7
	MOVF	EX_ADDR2,W
	BTFSS	STATUS,Z
	 GOTO	SYSEX_UNMATCH

	MOVF	PARAM1,W		; リセットモードが1以上の場合はリセットを行わない
	BTFSS	STATUS,Z
	 GOTO	SYSEX_UNMATCH

	CALL	system_reset
	GOTO	SYSEX_UNMATCH
;------------------------------------------------------------------------------
SYSEX_CHANNEL				; F0 7D 00 01 [00-0F] [PARAM1] .. F7
	MOVLW	16			; 16バイト目以降は無視する
	SUBWF	EX_ADDR2,W
	BTFSC	STATUS,C
	GOTO	SYSEX_UNMATCH

	MOVF	PARAM1,W		; 設定値が2以上の場合は値をセットせず次のアドレスへ進める
	SUBLW	1
	BTFSS	STATUS,C
	GOTO	SYSEX_INC_RETURN

	MOVLW	RX_CHANNEL		; 操作するフラグのアドレスを計算して値を格納
	ADDWF	EX_ADDR2,W
	MOVWF	FSR
	MOVF	PARAM1,W
	MOVWF	INDF
	
	GOTO	SYSEX_INC_RETURN	; 次のアドレスへ
;------------------------------------------------------------------------------
SYSEX_KEYASSIGN				; F0 7D 00 02 [00-7F] [PARAM1] .. F7
	MOVLW	128			; 128バイト目以降は無視する
	SUBWF	EX_ADDR2,W
	BTFSC	STATUS,C
	GOTO	SYSEX_UNMATCH

	MOVF	PARAM1,W		; 設定値が8以上の場合は値をセットせず次のアドレスへ進める
	SUBLW	7
	BTFSS	STATUS,C
	GOTO	SYSEX_INC_RETURN

	RRF	EX_ADDR2,W		; 格納先アドレスを計算
	ANDLW	0x7F
	ADDLW	KEYASSIGN
	MOVWF	FSR
	MOVLW	0xF0			; 格納先のデータをマスクして消す
	BTFSC	EX_ADDR2,0
	 MOVLW	0x0F
	ANDWF	INDF,F
	MOVF	PARAM1,W		; データをORで追記
	BTFSC	EX_ADDR2,0
	 SWAPF	PARAM1,W
	IORWF	INDF,F

	GOTO	SYSEX_INC_RETURN	; 次のアドレスへ
;------------------------------------------------------------------------------
SYSEX_CYCUT				; F0 7D 00 03 [00] [PARAM1] F7
	MOVF	EX_ADDR2,W
	BTFSS	STATUS,Z
	 GOTO	SYSEX_UNMATCH

	MOVF	PARAM1,W		; 設定値が5以上の場合は値をセットしない
	SUBLW	4
	BTFSS	STATUS,C
	 GOTO	SYSEX_UNMATCH

	MOVF	PARAM1,W
	MOVWF	CYCUT
	GOTO	SYSEX_UNMATCH
;------------------------------------------------------------------------------
SYSEX_SDWITH				; F0 7D 00 04 [00] [PARAM1] F7
	MOVF	EX_ADDR2,W
	BTFSS	STATUS,Z
	 GOTO	SYSEX_UNMATCH

	MOVF	PARAM1,W		; 設定値が8以上の場合は値をセットしない
	SUBLW	7
	BTFSS	STATUS,C
	 GOTO	SYSEX_UNMATCH

	MOVF	PARAM1,W
	MOVWF	SDWITH
	GOTO	SYSEX_UNMATCH
;------------------------------------------------------------------------------
SYSEX_VENDORRESET			; F0 7D 00 10 [00] [PARAM1] F7
	MOVF	EX_ADDR2,W
	BTFSS	STATUS,Z
	 GOTO	SYSEX_UNMATCH

	MOVF	PARAM1,W		; 設定値が2以上の場合は値をセットしない
	SUBLW	1
	BTFSS	STATUS,C
	 GOTO	SYSEX_UNMATCH

	BCF	PFLAG,VENDOR_RESET
	BTFSC	PARAM1,0
	 BSF	PFLAG,VENDOR_RESET
	GOTO	SYSEX_UNMATCH
;------------------------------------------------------------------------------
SYSEX_CLEARASSIGN			; F0 7D 00 11 [00] [PARAM1] F7
	MOVF	EX_ADDR2,W
	BTFSS	STATUS,Z
	 GOTO	SYSEX_UNMATCH

	MOVF	PARAM1,W
	BTFSS	STATUS,Z
	 GOTO	SYSEX_UNMATCH

	MOVLW	64
	MOVWF	TMP00
	MOVLW	KEYASSIGN
	MOVWF	FSR
SYSEX_CLEARASSIGN_L1
	CLRF	INDF
	INCF	FSR,F
	DECFSZ	TMP00,F
	 GOTO	SYSEX_CLEARASSIGN_L1
	GOTO	SYSEX_UNMATCH
;------------------------------------------------------------------------------
SYSEX_SAVEASSIGN			; F0 7D 00 12 [00] [PARAM1] F7
	MOVF	EX_ADDR2,W
	BTFSS	STATUS,Z
	 GOTO	SYSEX_UNMATCH

	MOVF	PARAM1,W		; 設定値が4以上の場合は実行しない
	SUBLW	3
	BTFSS	STATUS,C
	 GOTO	SYSEX_UNMATCH

	MOVF	PARAM1,W
	CALL	save_keyassign
	GOTO	SYSEX_UNMATCH
;------------------------------------------------------------------------------
; SYSEX_RESET_P2 メーカー別リセット検出ロジック
;------------------------------------------------------------------------------
SYSEX_RESET_P2
	MOVF	EX_ADDR2,W		; EX_ADDR2によってチェックする値を変更する
	MOVWF	TMP00
	MOVLW	LOW RESET_SEQUENCE_TABLE
	ADDWF	TMP00,F
	MOVLW	HIGH RESET_SEQUENCE_TABLE
	BTFSC	STATUS,C
	 ADDLW	1
	MOVWF	PCLATH
	MOVF	TMP00,W
	MOVWF	PCL
RESET_SEQUENCE_TABLE
	GOTO	RESEQ_42		; 0 Roland開始ポイント
	GOTO	RESEQ_12		; 1
	GOTO	RESEQ_40_BR_00		; 2 モードメッセージなら7へ分岐する
	GOTO	RESEQ_00		; 3
	GOTO	RESEQ_7F		; 4
	GOTO	RESEQ_00		; 5
	GOTO	RESEQ_EX_41		; 6 リセット実行
	GOTO	RESEQ_00		; 7
	GOTO	RESEQ_7F		; 8
	GOTO	RESEQ_00_BR_01		; 9 シングル/ダブルモードにより11へ分岐する
	GOTO	RESEQ_EX_01		; 10 リセット実行
	GOTO	RESEQ_EX_00		; 11 リセット実行
	GOTO	RESEQ_4C		; 12 Yamaha開始ポイント
	GOTO	RESEQ_00		; 13
	GOTO	RESEQ_00		; 14
	GOTO	RESEQ_7E		; 15
	GOTO	RESEQ_EX_00		; 16 リセット実行
	GOTO	RESEQ_09		; 17 GM開始ポイント
	GOTO	RESEQ_EX_01_02_03	; 18 リセット実行

RESEQ_00				; 値をチェック
	MOVLW	0x00
	GOTO	RESEQ_CHECK
RESEQ_09
	MOVLW	0x09
	GOTO	RESEQ_CHECK
RESEQ_12
	MOVLW	0x12
	GOTO	RESEQ_CHECK
RESEQ_42
	MOVLW	0x42
	GOTO	RESEQ_CHECK
RESEQ_4C
	MOVLW	0x4C
	GOTO	RESEQ_CHECK
RESEQ_7E
	MOVLW	0x7E
	GOTO	RESEQ_CHECK
RESEQ_7F
	MOVLW	0x7F
RESEQ_CHECK
	SUBWF	PARAM1,W
	BTFSC	STATUS,Z
	 GOTO	SYSEX_INC_RETURN	; 一致しているならば、チェックアドレスをインクリメントする
	GOTO	SYSEX_UNMATCH		; PARAM1と一致しないならば、PHASE_IGNOREでチェックを中断

RESEQ_40_BR_00				; 値をチェック、分岐
	MOVLW	0x40
	SUBWF	PARAM1,W
	BTFSC	STATUS,Z
	 GOTO	SYSEX_INC_RETURN	; PARAM1が0x40なら次へ
	MOVLW	0x00
	SUBWF	PARAM1,W
	BTFSS	STATUS,Z
	 GOTO	SYSEX_UNMATCH		; PARAM1が0x00と一致しない場合はPHASE_IGNOREでチェックを中断
	MOVLW	6
	MOVWF	EX_ADDR2		; 一致している場合は、現在を6として次に7が実行されるようにする。
	GOTO	SYSEX_INC_RETURN

RESEQ_00_BR_01
	MOVLW	0x00
	SUBWF	PARAM1,W
	BTFSC	STATUS,Z
	 GOTO	SYSEX_INC_RETURN	; PARAM1が0x00なら次へ
	MOVLW	0x01
	SUBWF	PARAM1,W
	BTFSS	STATUS,Z
	 GOTO	SYSEX_UNMATCH		; PARAM1が0x01と一致しない場合はPHASE_IGNOREでチェックを中断
	MOVLW	10
	MOVWF	EX_ADDR2		; 一致している場合は、現在を10として次に11が実行されるようにする。
	GOTO	SYSEX_INC_RETURN

RESEQ_EX_01_02_03			; 値をチェック、リセット
	MOVLW	0x01
	SUBWF	PARAM1,W
	BTFSC	STATUS,Z
	 GOTO	RESEQ_DO_RESET		; PARAM1が0x01ならリセット

	MOVLW	0x02
	SUBWF	PARAM1,W
	BTFSC	STATUS,Z
	 GOTO	RESEQ_DO_RESET		; PARAM1が0x02ならリセット

	MOVLW	0x03
	SUBWF	PARAM1,W
	BTFSC	STATUS,Z
	 GOTO	RESEQ_DO_RESET		; PARAM1が0x03ならリセット
	GOTO	SYSEX_UNMATCH		; PARAM1と一致しないならば、PHASE_IGNOREでチェックを中断

RESEQ_EX_41				; 値をチェック、リセット
	MOVLW	0x41
	GOTO	RESEQ_EX_CHECK
RESEQ_EX_01
	MOVLW	0x01
	GOTO	RESEQ_EX_CHECK
RESEQ_EX_00
	MOVLW	0x00
RESEQ_EX_CHECK
	SUBWF	PARAM1,W
	BTFSS	STATUS,Z
	 GOTO	SYSEX_UNMATCH		; PARAM1と一致しないならば、PHASE_IGNOREでチェックを中断
RESEQ_DO_RESET				; 一致しているならばリセット実行
	CLRF	PARAM1			; 各社リセットの時は、モノモードでリセットを行う。
	CALL	system_reset
	GOTO	SYSEX_RETURN		; system_resetでフラグとEVENTがリセットされるのでPHASE_IGNOREに移行する必要はない。
;------------------------------------------------------------------------------
; FIFO読み出しルーチン
;	バッファが空の場合は、エンプティフラグをセットしてリターンする。
;
; 引数
;	なし
;
; 戻り値
;	W			読み出したデータ
;	FIFOSTATUS-FIFO1EMPTY	FIFOバッファエンプティフラグ
;
; 使用変数
;	なし
;------------------------------------------------------------------------------
fifo1read
	MOVF	USEDSIZE1,W
	BTFSS	STATUS,Z		; バッファエンプティチェック
	 GOTO	FIFO1READ1
	BSF	FIFOSTATUS,FIFO1EMPTY	; 空フラグを立ててリターン
	RETURN
FIFO1READ1
	BCF	FIFOSTATUS,FIFO1EMPTY	; 空フラグOFF
	BCF	STATUS,IRP		; ◆ IRP=0
	MOVF	READPTR1,W		; バッファを間接アクセス
	MOVWF	FSR
	MOVF	INDF,W
	MOVWF	READDATA
	DECF	USEDSIZE1,F		; FIFOのデータ数を-1(呼び出し時にチェックをしているので、マイナスになることはない)
	INCF	READPTR1,F		; 読み出しポインタをインクリメント
FIFO1READ2
	MOVLW	FIFOBUF1		; ポインタのロールオーバーのチェック
	ADDLW	FIFO1SIZE
	SUBWF	READPTR1,W
	BTFSS	STATUS,Z
	 GOTO	FIFO1READ3		; そのままリターン
	MOVLW	FIFOBUF1		; ロールオーバーしていたならば、先頭位置に移動
	MOVWF	READPTR1
FIFO1READ3
	MOVF	READDATA,W		; Wに読み込みデータを入れてリターン
	RETURN
;------------------------------------------------------------------------------
; FIFO書き込みルーチン
;	バッファがいっぱいの場合は、データを書き込まずにフルフラグをセットしてリターンする。
;
; 引数
;	W		書き込むデータ(破壊)
;
; 戻り値
;	FIFOSTATUS-FIFO1FULL	FIFOバッファフルフラグ
;
; 使用変数
;	なし
;------------------------------------------------------------------------------
fifo1write
	MOVWF	WRITEDATA1		; Wに入っている書き込みデータを退避
	MOVLW	FIFO1SIZE		; バッファフルチェック
	SUBWF	USEDSIZE1,W
	BTFSS	STATUS,Z
	 GOTO	FIFO1WRITE1
	BSF	FIFOSTATUS,FIFO1FULL	; フルフラグを立ててリターン
	RETURN
FIFO1WRITE1
	BCF	FIFOSTATUS,FIFO1FULL	; フルフラグOFF
	BCF	STATUS,IRP		; ● IRP=0
	MOVF	WRITEPTR1,W		; バッファを間接アクセス
	MOVWF	FSR
	MOVF	WRITEDATA1,W
	MOVWF	INDF
	INCF	USEDSIZE1,F		; データ量を+1(呼び出し時にチェックをしているので、データ数オーバーになることはない)
	INCF	WRITEPTR1,F
FIFO1WRITE2
	MOVLW	FIFOBUF1		; ポインタのロールオーバーのチェック
	ADDLW	FIFO1SIZE
	SUBWF	WRITEPTR1,W
	BTFSS	STATUS,Z
	 RETURN				; そのままリターン
	MOVLW	FIFOBUF1		; ロールオーバーしていたならば、先頭位置に移動
	MOVWF	WRITEPTR1
	RETURN
;------------------------------------------------------------------------------
; キーマップの読み出し、保存
; 引数
;	W	マップ番号0-3
; 使用変数
;	TMP22(トリガオフとかぶらないようにする)
;------------------------------------------------------------------------------
load_keyassign
	MOVWF	TMP22			; マップ番号からEEPROMアドレスに変換
	SWAPF	TMP22,F
	RLF	TMP22,F
	RLF	TMP22,W
	ANDLW	0xC0
	BSF	STATUS,RP1		; ◆ RP=2
	MOVWF	EEADR

	BCF	STATUS,IRP		; ● IRP=0
	MOVLW	KEYASSIGN
	MOVWF	FSR
	MOVLW	64			; 64バイト分ループ
	MOVWF	TMP22
LOADKEY_LOOP
	BSF	STATUS,RP0		; ◆ RP=3
	BCF	EECON1,EEPGD
	BSF	EECON1,RD
	BCF	STATUS,RP0		; ◆ RP=2
	MOVF	EEDATA,W
	MOVWF	INDF
	INCF	FSR,F
	INCF	EEADR,F
	DECFSZ	TMP22,F
	 GOTO	LOADKEY_LOOP
	BCF	STATUS,RP1		; ◆ RP=0
	RETURN

save_keyassign
	MOVWF	TMP22			; マップ番号からEEPROMアドレスに変換
	SWAPF	TMP22,F
	RLF	TMP22,F
	RLF	TMP22,W
	ANDLW	0xC0
	BSF	STATUS,RP1		; ◆ RP=2
	MOVWF	EEADR

	BCF	STATUS,IRP		; ● IRP=0
	MOVLW	KEYASSIGN
	MOVWF	FSR
	MOVLW	64			; 64バイト分ループ
	MOVWF	TMP22
SAVEKEY_LOOP
	MOVF	INDF,W
	MOVWF	EEDATA
	BSF	STATUS,RP0		; ◆ RP=3
	BCF	EECON1,EEPGD
	BSF	EECON1,WREN
	BCF	INTCON,GIE
	MOVLW	0x55
	MOVWF	EECON2
	MOVLW	0xAA
	MOVWF	EECON2
	BSF	EECON1,WR
EEPWRITEPOLL
	BTFSC	EECON1,WR		; 書き込みが終わるまでループ
	 GOTO	EEPWRITEPOLL
	BSF	INTCON,GIE
	BCF	EECON1,WREN
	BCF	STATUS,RP0		; ◆ RP=2
	INCF	FSR,F
	INCF	EEADR,F
	DECFSZ	TMP22,F
	 GOTO	SAVEKEY_LOOP
	BCF	STATUS,RP1		; ◆ RP=0
	RETURN
;------------------------------------------------------------------------------
; トリガオフ
;  割り込みルーチンから呼ばれる
;
; 使用変数
;   TMP20,21,23(out_port,out_bit)
;------------------------------------------------------------------------------
trigger_off
	BCF	STATUS,IRP		; ◆ IRP=0
	MOVLW	OFF_CUT			; 減算のため最後から処理する
	MOVWF	FSR
	MOVLW	8
	MOVWF	TMP20
OFF_LOOP
	MOVF	INDF,W
	BTFSC	STATUS,Z
	 GOTO	OFF_NEXT		; トリガOFF済みは処理しない
	DECFSZ	INDF,F
	 GOTO	OFF_NEXT		; トリガOFFのタイミングに満たない時は処理しない
	MOVF	FSR,W			; インストのポインタを退避
	MOVWF	TMP21
	MOVF	TMP20,W			; 出力バッファを取得し、
	CALL	out_port
	MOVWF	FSR
	MOVF	TMP20,W			; 対象のビットを落とす
	CALL	out_bit
	XORLW	0xFF
	ANDWF	INDF,F
	MOVF	TMP21,W			; インストのポインタを復元
	MOVWF	FSR
OFF_NEXT
	DECF	FSR,F
	DECFSZ	TMP20,F
	 GOTO	OFF_LOOP
	MOVF	PORTA_,W		; 最終結果を出力
	MOVWF	PORTA
	MOVF	PORTB_,W
	MOVWF	PORTB
	RETURN
;------------------------------------------------------------------------------
; PCLテーブル
;------------------------------------------------------------------------------
	ORG	0x3E6
out_port				; ビットを操作するバッファのアドレスを返す
	MOVWF	TMP23
	MOVLW	HIGH $
	MOVWF	PCLATH			; ■ PCLATH
	DECF	TMP23,W
	ADDWF	PCL,F
	RETLW	PORTA_			; 1 BD
	RETLW	PORTB_			; 2 SD
	RETLW	PORTB_			; 3 LT
	RETLW	PORTB_			; 4 HT
	RETLW	PORTB_			; 5 CL
	RETLW	PORTB_			; 6 CY
	RETLW	PORTB_			; 7 HH
	RETLW	PORTA_			; 8 CUT

out_bit					; 操作するビットのマスクパターンを返す
	MOVWF	TMP23
	MOVLW	HIGH $
	MOVWF	PCLATH			; ■ PCLATH
	DECF	TMP23,W
	ADDWF	PCL,F
	RETLW	B'00000001'		; 1 BD PORTA
	RETLW	B'00010000'		; 2 SD
	RETLW	B'01000000'		; 3 LT
	RETLW	B'10000000'		; 4 HT
	RETLW	B'00000001'		; 5 CL
	RETLW	B'00000010'		; 6 CY
	RETLW	B'00001000'		; 7 HH
	RETLW	B'00010000'		; 8 CUT

;------------------------------------------------------------------------------
; データEEPROM
;------------------------------------------------------------------------------
	ORG	2100H
		; 0=na 1=BD 2=SD 3=LT 4=HT 5=CL 6=CY 7=HH
		; cC   dD   FE   Gf   Ag   Ba
; Map0 #124 クリア
	DE	0x00,0x00,0x00,0x00,0x00,0x00	;-1(0)
	DE	0x00,0x00,0x00,0x00,0x00,0x00	; 0(12)
	DE	0x00,0x00,0x00,0x00,0x00,0x00	; 1(24)
	DE	0x00,0x00,0x00,0x00,0x00,0x00	; 2(36)
	DE	0x00,0x00,0x00,0x00,0x00,0x00	; 3(48)
	DE	0x00,0x00,0x00,0x00,0x00,0x00	; 4(60)
	DE	0x00,0x00,0x00,0x00,0x00,0x00	; 5(72)
	DE	0x00,0x00,0x00,0x00,0x00,0x00	; 6(84)
	DE	0x00,0x00,0x00,0x00,0x00,0x00	; 7(96)
	DE	0x00,0x00,0x00,0x00,0x00,0x00	; 8(108)
	DE	0x00,0x00,0x00,0x00		; 9(120)
; Map1 #125 クリア
	DE	0x00,0x00,0x00,0x00,0x00,0x00	;-1(0)
	DE	0x00,0x00,0x00,0x00,0x00,0x00	; 0(12)
	DE	0x00,0x00,0x00,0x00,0x00,0x00	; 1(24)
	DE	0x00,0x00,0x00,0x00,0x00,0x00	; 2(36)
	DE	0x00,0x00,0x00,0x00,0x00,0x00	; 3(48)
	DE	0x00,0x00,0x00,0x00,0x00,0x00	; 4(60)
	DE	0x00,0x00,0x00,0x00,0x00,0x00	; 5(72)
	DE	0x00,0x00,0x00,0x00,0x00,0x00	; 6(84)
	DE	0x00,0x00,0x00,0x00,0x00,0x00	; 7(96)
	DE	0x00,0x00,0x00,0x00,0x00,0x00	; 8(108)
	DE	0x00,0x00,0x00,0x00		; 9(120)
; Map2 #126 クリア
	DE	0x00,0x00,0x00,0x00,0x00,0x00	;-1(0)
	DE	0x00,0x00,0x00,0x00,0x00,0x00	; 0(12)
	DE	0x00,0x00,0x00,0x00,0x00,0x00	; 1(24)
	DE	0x00,0x00,0x00,0x00,0x00,0x00	; 2(36)
	DE	0x00,0x00,0x00,0x00,0x00,0x00	; 3(48)
	DE	0x00,0x00,0x00,0x00,0x00,0x00	; 4(60)
	DE	0x00,0x00,0x00,0x00,0x00,0x00	; 5(72)
	DE	0x00,0x00,0x00,0x00,0x00,0x00	; 6(84)
	DE	0x00,0x00,0x00,0x00,0x00,0x00	; 7(96)
	DE	0x00,0x00,0x00,0x00,0x00,0x00	; 8(108)
	DE	0x00,0x00,0x00,0x00		; 9(120)
; Map3 #127 GM準拠 リセット時にロードする
	DE	0x00,0x00,0x00,0x00,0x00,0x00	;-1(0)
	DE	0x00,0x00,0x00,0x00,0x00,0x00	; 0(12)
	DE	0x00,0x00,0x00,0x00,0x00,0x10	; 1(24)
	DE	0x01,0x02,0x32,0x37,0x47,0x46	; 2(36)
	DE	0x64,0x64,0x66,0x60,0x60,0x60	; 3(48)
	DE	0x00,0x00,0x00,0x00,0x00,0x00	; 4(60)
	DE	0x00,0x50,0x00,0x00,0x00,0x00	; 5(72)
	DE	0x00,0x00,0x00,0x00,0x00,0x00	; 6(84)
	DE	0x00,0x00,0x00,0x00,0x00,0x00	; 7(96)
	DE	0x00,0x00,0x00,0x00,0x00,0x00	; 8(108)
	DE	0x00,0x00,0x00,0x00		; 9(120)

	END