;------------------------------------------------------------------------------
; IRKey/PS2 - 赤外線リモコン to PS/2キーボードコンバータ
;------------------------------------------------------------------------------
; バージョン履歴
; 2019/11/17 Ver.0.46	DIMMERをEQU定義にした、システムリセットでCONFIG1をクリアしないようにした。
; 2019/11/04 Ver.0.45a  DAで文字列が展開できることを初めて知った。
; 2019/09/01 Ver.0.45	3つのステータスLEDが消えているときにカラーLEDを減光するようにした。
;			記憶可能サイズより大きいデータの受信機能を削除。
; 2019/08/31 Ver.0.44	IRバッファの位置変更
;			受信可能ビット数増加、USART送信可能ビット数増加
;			SETで記憶可能サイズより大きいデータを受信した場合、メッセージを表示して入力をやり直すようにした。
; 2019/08/31 Ver.0.43a	フラッシュワークエリアの扱いを変更、PS/2の監視とCLRWDTを共通化
; 2019/08/25 Ver.0.43	受信する赤外線の最大ビット数が間違っていた。
; 2019/08/25 Ver.0.42	setでIRデータが0ビットの時は、キャンセルではなく再入力とした。緑LED点灯状態を変更。
; 2019/08/16 Ver.0.41	ターミナルで連続してsetコマンドを流すとUSARTが停止することがあった。
;			フラッシュの消去・書き込みはCPUコアが停止するため、USARTのオーバーランエラーになるのが原因だった。
;			erase_flash, write16_flash実行時はUSARTを停止するようにした。
;			ただし垂れ流しになるので、ターミナル側で20ms程度の行間タイマーを入れること。
; 2019/08/16 Ver.0.40	PS/2でPCからRESENDが3回送られてきたら、その回だけ応答せずに無限ループを防ぐようにした。
;			Ver.0.39で実装した自動Disabledはコメントアウトして廃止。
;			Timer0タイムアウトを利用して、TEMPORARILY_DISABLEも解除するようにした。
;			これは、PCからのコマンドに対するACK(FAh)がPCに届かなかった時、オプションバイトが送られて来ずに
;			TEMPORARILY_DISABLEがいつまでたっても解除できないのを回復させるための手段として実装した。
;			LEDがシアンで停止する場合、リモコンを押して戻ればパリティ系のエラー、PS2CLKが点きっぱなしならフレーミングエラー回復中と判断できる。
;			ROMの空きがゼロバイトになった。
; 2019/08/16 Ver.0.39	PS/2でPCからRESENDが3回送られてきたら、自動的にDISABLEDに入って無限ループしないようにした。
;			PS2_TRANSMITTEDを監視する低レベルの無限ループには効果は無い。
; 2019/08/16 Ver.0.38	PS/2で正常に受信できなかった場合は、自動的にRESENDコマンドを送るようにした。
;			PCが不正なデータを無限に送ってくると本来は無限ループに陥るが、
;			PC側はACKをチェックしているので3回リトライしても駄目なら送信を停止するようだ。
; 2019/08/16 Ver.0.37	TEMPORARILY_DISABLEフラグの扱いがおかしかった。
; 2019/08/15 Ver.0.36	setコマンドでリモコンコードを手入力できるようにした。
;			トレースでリモコンコードのマッチ番号を表示するようにした。
; 2019/08/15 Ver.0.35c	ps2_traceまで見直し。ps2_recieveとps2_sendは後回し。
;			設定画面でバンク切り替えできるbankコマンドを追加。
; 2019/08/15 Ver.0.35b	末尾からBANKCHANGEまで見直し。ROMを空けるためにメッセージの修正と細かくサブルーチン化。
;			16進文字列化ルーチンが何故か2個あったので削除。
; 2019/08/15 Ver.0.35a	PS2のRESEND用にデータを保存、送信するように変更。
;			末尾からinit_flashまで見直し。
; 2019/08/15 Ver.0.35	setコマンドでIRのデータ長に応じてエディタの最大編集文字数を制限するようにした。
;			RESULTを変数からフラグに変更。
;			末尾からhex8までプログラムの見直し。
; 2019/08/14 Ver.0.34	IBMの資料を元にPS/2受信時の一時的なスキャン停止を実装。
; 2019/08/14 Ver.0.33	decimal8のスペース変換手順を変更。
;			リピートタイムアウトの無効化方法を変更。
;			ir_traceのリピート表示を変更。
;			TypematicのRate/Delayに対応した。ただし、リピートコードを送るリモコンに限る。
; 2019/08/12 Ver.0.32	安定版
;			送信衝突時のデータ抜けは、E0h/E1hを重複して送れない点に起因していた。これはWindowsの仕様なのかよく解らない。
;			コンティニュー時はまず00hを送るようにした。
;			PAUSEは14hを送ればいきなりPAUSEと判定される。77hは送っても送らなくてもどちらでも良いようだ。これもWindowsの仕様なのかよく解らない。
;			PAUSEは良い実装方法が浮かばないので未サポートとし、Ctrl+NumLockと手入力して送る事にする。
; 2019/08/11 Ver.0.31	安定版
;			PS2データレジスタを送受信別々に設けた。
;			割り込みハンドラのスタックを削減するため、CALLからGOTO呼び出しに変更。
;			PS/2のマルチバイト受信をイベント方式に変更。(MIDI解析に似せた)
;			1カ所でしか行っていないFIFOの残量チェックを呼び出し元でチェックするようにしてfifo_readから省きFIFO_STATUSを廃止。
;			fifo_readでUSEDSIZEの変更タイミングの見直し。
;			データリストで登録されていない番号を非表示にした。
;			データリストとフリーエリア検索で未使用と判定される使用サイズの基準を変更。
;			setコマンドでCtrl+Cでキャンセルできるようにした。Ctrl+Uは廃止。
;			ソフトウエアウォッチドッグタイマを有効にした。
;			PS/2受信高負荷時の送信衝突データ抜けが稀に起こる。
; 2019/08/04 Ver.0.30	安定版
;			キーデータ送信中にRTSで中断した場合の再開機能を付けた。
;			read_flashから戻った時にWにもLSB側の値が入るように変更。
;			ir_traceに間に合わせのリピート表示を付けた。
;			POR/BATのタイマーを400msに変更。
; 2019/08/04 Ver.0.29	データ破棄タイマーをのばした。長いキーストロークの時に勝手にデータが破棄されていた。
; 2019/08/03 Ver.0.28	安定版
;			ps2_sendでinhibitを自動再送信するようにした。
; 2019/08/02 Ver.0.27	キーコードの登録・送信方法を変更した。メモリの空きが無くなった。
; 2019/08/01 Ver.0.26	安定版
;			カラーLEDの光る色を整理した。
; 2019/07/30 Ver.0.25	機能の整理、基板を作った。
; 2019/07/29 Ver.0.24	データ登録ができるようになった。リピート無効化タイマーTMR0でIR_LENGTHがリセットされてしまった。
; 2019/07/28 Ver.0.23	フラッシュワークエリアを新設
; 2019/07/28 Ver.0.22	文字列→バイナリ変換実装
; 2019/07/27 Ver.0.21	コマンドライン編集を一部実装した
; 2019/07/25 Ver.0.20	send_keydataでINHIBITのチェックをしていなかったのでキーデータが正しく送られていなかった。
;			デバッグコードを除去したため連続してps2_sendを呼ぶと直後のINHで必ずリトライがかかる。必ずINHチェックを行う事。
;			DISABLEの時にバンクLEDを消灯するようにした。
; 2019/07/25 Ver.0.19a	USART受信FIFO作成、単純エコーバックを行う。
; 2019/07/22 Ver.0.19	デバッグコード除去、バンクチェンジ・search_dataのデータ引き継ぎができていなかった、
; 2019/07/21 Ver.0.18	デバッグ用コードを一部除去、PS/2周りのルーチン変更。
; 2019/07/20 Ver.0.17	バンク変更を実装
; 2019/07/20 Ver.0.16	データ初期化、データ削除ルーチン作成
; 2019/07/20 Ver.0.15	登録リスト表示ルーチン作成、サブルーチンの微調整
; 2019/07/15 Ver.0.14	デバッグメッセージ追加
; 2019/07/14 Ver.0.13	リピートのデータ破棄用タイマーを付けた
; 2019/07/14 Ver.0.12	PS2送信中にPCからの要求をうまく受け付けられていなかった。
; 2019/07/14 Ver.0.11	リモコン操作ができるようになった。
; 2019/07/08 Ver.0.10	PS/2の後続バイト受信完了のポーリング忘れ、INT0IFクリア忘れ、INT0系のフラグ名間違い
; 2019/07/07 Ver.0.09	PS/2の通信ができるようになった。
; 2019/07/07 Ver.0.08	PS/2のデータ読み出しができるようになった。
; 2019/07/06 Ver.0.07	プログラムの見直し
; 2019/06/30 Ver.0.06	リモコンを受信した数値が表示できるようになった。Cから移植するのは大変だ。
; 2019/06/29 Ver.0.05b	割り込みを禁止していてもフラグだけが立つのがややこしい。
; 2019/06/29 Ver.0.05a	とりあえずキャプチャして数値を表示。
; 2019/06/29 Ver.0.05	カウントアップさせた数値を無限に送信。
; 2019/06/29 Ver.0.04	文字列バッファをUSARTで割り込み送信する。
; 2019/06/26 Ver.0.03	作り直し、USART垂れ流し送信。
; 2019/06/01 Ver.0.02	USART割り込み受信でエコーバック。
; 2019/06/01 Ver.0.01	USART垂れ流し送信。
;------------------------------------------------------------------------------
; IRP,RP1,RP0を切り替える所で◆ RP=, ● IRP=をコメントに置く
; PCLATHを切り替える所で■ PCLATHをコメントに置く
; サブルーチンからの戻りの時、
;  RP1,RP0は0に戻してリターンする。
;  IRP,PCLATHは戻さずにリターンする。
; ルーチン呼び出しの時もRP=0を基本として記述している。
;------------------------------------------------------------------------------
; デバイス	PIC16F88
; 動作周波数	Ext20MHz
;------------------------------------------------------------------------------
	LIST		P=PIC16F88,R=DEC,N=0
	INCLUDE		"P16F88.INC"
	ERRORLEVEL	0,-302,-306,-307
	__CONFIG _CONFIG1, 0x2F62
					; FOSC : HS
					; WDTE : Disable
					; PWRTE: Enable
					; MCLRE: RA5=MCLR
					; BODEN: Enable
					; LVP  : RB3=I/O
					; CPD  : Not Protect
					; WRT  : Protection off
					; DEBUG: Disable
					; CCPMX: CCP1=RB3
					; CP   : Not Protect
	__CONFIG _CONFIG2, 0x3FFC
					; FCMEN: Disable
					; IESO : Disable
;------------------------------------------------------------------------------
; 変数宣言
;------------------------------------------------------------------------------
KD_ADDRL	EQU	0x20		; 送信中のキーコードのアドレス
KD_ADDRH	EQU	0x21		;
KD_SIZE		EQU	0x22		; 送信すべきキーコードの数
KD_COUNT	EQU	0x23		; 送信すべきキーコードの残数
KD_INCR		EQU	0x24		; キーコードアドレスの増減定数、送信中断の有無
KD_KEYCODE	EQU	0x25		; 読み出したキーコード

PS2_TXLAST	EQU	0x26		; RESEND用最終送信データ
PS2_RESENDCOUNT	EQU	0x27		; リトライカウンタ
PS2_TXDATA	EQU	0x28
PS2_RXDATA	EQU	0x29
PS2_STATUS	EQU	0x2A
PS2_LASTCMD	EQU	0x2B		; PS/2から最後に受信したコマンドバイト
PS2_DELAY	EQU	0x2C		; IRリピートが来た時の無視回数
PS2_RATE	EQU	0x2D		; IRリピートが来た時の間引き数
PS2_DCOUNT	EQU	0x2E		; それぞれのカウンタ
PS2_RCOUNT	EQU	0x2F

IR_STATUS	EQU	0x30		; IR受信用変数
IR_FORMAT	EQU	0x31
IR_LENGTH	EQU	0x32
PW0_L		EQU	0x33
PW0_H		EQU	0x34
PW1_L		EQU	0x35
PW1_H		EQU	0x36
THRESHOLD_L	EQU	0x37
THRESHOLD_H	EQU	0x38
BITMASK		EQU	0x39
MASKDATA	EQU	0x3A
NEW_FORMAT	EQU	0x3B
DESTROY_COUNT	EQU	0x3C

USART_TXPTR	EQU	0x40
USART_STATUS	EQU	0x41
READPTR		EQU	0x42
WRITEPTR	EQU	0x43
USEDSIZE	EQU	0x44


ED_MAXLEN	EQU	0x47
ED_CMDLEN	EQU	0x48
ED_STATUS	EQU	0x49

FLAGS		EQU	0x50
COLOR		EQU	0x52
CONFIG1		EQU	0x53		; システム設定データ
DATANUMBER	EQU	0x54		; IRデータベースのデータ番号


MATH00		EQU	0x68		; LSB 四則演算用変数
MATH01		EQU	0x69		; MSB
MATH10		EQU	0x6A
MATH11		EQU	0x6B
MATH_TEMP	EQU	0x6C

INTSRC		EQU	0x6E		; 割り込みマスクと要因のAND
ITMP0		EQU	0x6F		; 割り込みルーチン用一時使用変数

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

FLASH_WORK	EQU	0x0B0		;-0x0CF 32バイト フラッシュ保存ワークエリア
IR_BUFFER	EQU	0x0D0		;-0x0EF 32バイト IR受信バッファ
TX_BUFFER	EQU	0x110		;-0x16F 96バイト USART送信バッファ
CMP_BUFFER	EQU	0x190		;-0x19F 16バイト 文字列照合バッファ
RX_FIFOTOP	EQU	0x1A0		;-0x1AF 16バイト USART受信FIFO
RX_FIFOLAST	EQU	0x1AF
EDITOR_BUFFER	EQU	0x1B0		;-0x1EF 64バイト 行編集バッファ

;------------------------------------------------------------------------------
; 定数宣言
;------------------------------------------------------------------------------
BLACK		EQU	0		; LEDカラー R:bit1 G:bit4 B:bit0
RED		EQU	0x02
GREEN		EQU	0x10
YELLOW		EQU	RED + GREEN
BLUE		EQU	0x01
MAGENTA		EQU	RED + BLUE
CYAN		EQU	GREEN + BLUE
WHITE		EQU	RED + GREEN + BLUE
DIMMER_OFF	EQU	8
DIMMER_SET	EQU	32

NULL		EQU	0		; 特殊文字
ETX		EQU	3		; Ctrl+C
BS		EQU	8
LF		EQU	0x0A
CR		EQU	0x0D
ESC		EQU	0x1B
DEL		EQU	0x7F
CRLF            EQU     0x068A
LFNULL          EQU     0x0500

DEFAULT_DELAY	EQU	4		; デフォルトのディレイ間引き数
DEFAULT_RATE	EQU	1		; デフォルトのレート間引き数

ED_ENTER	EQU	0
ED_CANCEL	EQU	1

DESTROY_TIMER	EQU	20
RESENDMAX	EQU	4		; 何回目のRESEND要求受信でギブアップするか

FIFOSIZE	EQU	RX_FIFOLAST-RX_FIFOTOP+1
EDITOR_BUFFER_SIZE EQU	64

BANKSELECT	EQU	0		; CONFIG1

NODATA		EQU	7		; DATANUMBER

DISABLED	EQU	0		; FLAGS
TEMPORARILY_DISABLE EQU	1
REQ_RESET	EQU	2
REPLACE_ZERO	EQU	3
INDENT		EQU	4		; 0=DEL 1=SPACE
RESULT		EQU	5

LED_NUM		EQU	2		; PORTA 負論理
LED_CAPS	EQU	3		; PORTA 負論理
LED_SCROLL	EQU	4		; PORTA 負論理

SCLK		EQU	0		; PORTB
SDATA		EQU	1

LED_GREEN	EQU	4		; PORTB

PS2_PERR	EQU	0		; PS2_STATUS 受信ステータス
PS2_FERR	EQU	1
;		EQU	2
PS2_RECIEVED	EQU	3
PS2_RTS		EQU	4		; PS2_STATUS 送信ステータス
PS2_INHIBIT	EQU	5
;		EQU	6
PS2_TRANSMITTED	EQU	7

TXING		EQU	0		; USART_STATUS

IDLE		EQU	0		; IR_STATUS
RECIEVING	EQU	1		; IR_STATUS
RECIEVED	EQU	2		; IR_STATUS

UNDEF		EQU	0		; IR_FORMAT
AEHA		EQU	1		; IR_FORMAT
NEC		EQU	2		; IR_FORMAT
SONY		EQU	3		; IR_FORMAT
REPEAT		EQU	0x10		; IR_FORMAT

IRMAXLEN	EQU	120		; IR最大保存ビット数
MIN_PERCENT	EQU	75
MAX_PERCENT	EQU	133

T_AEHA		EQU	4250 / 2	; 1Tの時間
T_NEC		EQU	5620 / 2
T_SONY		EQU	6000 / 2

L1_AEHA		EQU	T_AEHA * 8	; リーダーのメーク時間
L1_NEC		EQU	T_NEC  * 16
L1_SONY		EQU	T_SONY * 4

L2_AEHA		EQU	T_AEHA * 4	; リーダーのブレーク時間
L2_NEC		EQU	T_NEC  * 8
L2_SONY		EQU	T_SONY * 1

R_AEHA		EQU	T_AEHA * 8	; リピートリーダーのブレーク時間
R_NEC		EQU	T_NEC  * 4

D1MAX_AEHA	EQU	T_AEHA * 3 / 2	; データのメーク時間の最大(1.5T)
D1MAX_NEC	EQU	T_NEC  * 3 / 2	; D1MAX <= x <= THRESHOLDまでの中途半端な時間は無効データになる点に注意せよ
D1MAX_SONY	EQU	T_SONY * 3 / 2

D2MAX_AEHA	EQU	T_AEHA * 4	; データのブレーク時間の最大
D2MAX_NEC	EQU	T_NEC  * 4
D2MAX_SONY	EQU	T_SONY * 5 / 2	; 2.5T
;------------------------------------------------------------------------------
; 電源ON
;------------------------------------------------------------------------------
	ORG	0
	MOVLW	B'00011100'		; I/O初期化 未使用ポートは0を出力
	MOVWF	PORTA			; [76]CLOCK  [5]MCLR [4]/LED_SCROLL [3]/LED_CAPS [2]/LED_NUM [1]LED_RED [0]LED_BLUE
	CLRF	PORTB			; [76]未使用 [5]USART_TX [4]LED_GREEN [3]IR_CCP [2]USART_RX [1]PS2_DATA [0]PS2_CLOCK
	GOTO	POWER_ON
;------------------------------------------------------------------------------
; 割り込み分岐
;------------------------------------------------------------------------------
	ORG	4
	MOVWF	W_			; Wセーブ
	SWAPF	STATUS,W		; STATUSセーブ
	MOVWF	STATUS_
	MOVF	PCLATH,W		; PCLATHセーブ
	MOVWF	PCLATH_
	MOVF	FSR,W			; FSRセーブ
	MOVWF	FSR_

	CLRF	STATUS			; ● IRP=0
	BSF	STATUS,RP0		; ◆ RP=1
	MOVF	PIE1,W
	BCF	STATUS,RP0		; ◆ RP=0
	ANDWF	PIR1,W			; PIEが0でもPIRが立つので、
	MOVWF	INTSRC			;  ANDを取って不要な割り込みハンドラへの分岐を防ぐ。

	BTFSC	INTSRC,CCP1IF		; この呼び出しはクロック補正をするため位置を動かしてはいけない。
	 GOTO	ccp1_handler
CCP1_RETURN

	BTFSC	INTSRC,TMR1IF		; 割り込み中に他の割り込みが来るかもしれないので、
	 GOTO	timer1_handler		; 念のためすべての要因をチェックする。
TMR1_RETURN

	BTFSC	INTSRC,TXIF
	 GOTO	usart_tx_handler
UTX_RETURN
	
	BTFSC	INTSRC,RCIF
	 GOTO	usart_rx_handler
URX_RETURN
	
	RLF	INTCON,W		; INTCONも同様
	MOVWF	INTSRC
	SWAPF	INTSRC,F
	MOVF	INTCON,W
	ANDWF	INTSRC,W
	ANDLW	7
	MOVWF	INTSRC
	
	BTFSC	INTSRC,INT0IF
	 GOTO	int0_handler
INT0_RETURN

	BTFSC	INTSRC,TMR0IF
	 GOTO	timer0_handler
TMR0_RETURN

	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
	BSF	STATUS,RP0		; ◆ RP=1
	CLRF	ANSEL			; PORTAはデジタルI/O
	CLRF	TRISA
	MOVLW	B'00101111'
	MOVWF	TRISB

	MOVLW	B'01010111'		; PORTBプルアップ有効、PS2クロック立ち上げ割り込み、TMR0内部クロック、プリスケーラ1:256
	MOVWF	OPTION_REG

	MOVLW	B'00100110'		; USART
	MOVWF	TXSTA			; TXENは常にONで使い、TXIEのみで送信制御する。
	MOVLW	10			; 115,200bps
	MOVWF	SPBRG

	BCF	STATUS,RP0		; ◆ RP=0
	BSF	RCSTA,SPEN		; USART有効 受信はまだ開始しない
	
	BSF	T1CON,TMR1ON		; TIMER1有効 タイムアウト:13.1ms
	
	MOVLW	B'00000100'		; CCP
	MOVWF	CCP1CON			; 立下りモードから開始
	
	MOVLW	WHITE			; ウォッチドッグの再起動なら白を点灯させる
	BTFSS	STATUS,NOT_TO
	 CALL	set_color

	MOVLW	50			; キーボードPORフェーズ 500ms
	CALL	wait10ms
;------------------------------------------------------------------------------
; システムリセット、メインループ前処理、PS/2 RESETでもここに飛ばす
;------------------------------------------------------------------------------
	CLRF	CONFIG1			; CONFIGは必要に応じてEEPROMからロードしても良い
SYSTEM_RESET				; キーボードBAT開始 400ms
	CLRF	USART_STATUS		; タイトル表示の前にUSARTステータスだけ初期化しておく
	MOVLW	B'11000000'		; 全割り込み停止(TMR0IE/INT0IE)、GIE,PEIE有効
	MOVWF	INTCON
	BCF	RCSTA,CREN		; USART受信停止
	BSF	STATUS,RP0		; ◆ RP=1
	CLRF	PIE1			; 全割り込み停止(RCIE/TXIE/CCP1IE/TMR1IE)
	BCF	STATUS,RP0		; ◆ RP=0

	CLRF	PORTA			; リセット中のLEDパターン
	MOVLW	MAGENTA			; NUM+CAPS+SCR+マゼンタ
	CALL	set_color

	MOVLW	40			
	CALL	wait10ms
	MOVLW	B'00011100'		; BAT完了のLEDパターン
	IORWF	PORTA,F			; マゼンタのみ

	CALL	set_default

	MOVLW	LOW RX_FIFOTOP		; USART RX_FIFO初期化
	MOVWF	READPTR
	MOVWF	WRITEPTR
	CLRF	USEDSIZE

	CLRF	IR_LENGTH
	CLRF	IR_FORMAT
	
	MOVLW	EDITOR_BUFFER_SIZE-1
	MOVWF	ED_MAXLEN
	CLRF	ED_CMDLEN

	CLRF	PS2_STATUS
	CLRF	KD_INCR			; 0以外の時はキーデータ送信中断扱いになる
	MOVLW	RESENDMAX
	MOVWF	PS2_RESENDCOUNT

	CLRF	FLAGS

	MOVLW	0xAA			; BAT Complete送信
	MOVWF	PS2_TXDATA		; Windows上ではL_Shift UPとして認識される
	CALL	ps2_send		; キーボードを認識しておらずクロックを止められている場合は
	BTFSS	PS2_STATUS,PS2_TRANSMITTED ; フルカラーLEDがマゼンタのまま、ここで無限ループする
	 GOTO	$-2

	MOVLW	LOW txt_title
	CALL	txstring
	MOVLW	LOW txt_prompt
	CALL	txstring
	CALL	usart_tx_sync
	
	BSF	RCSTA,CREN		; USART受信スタート
	BSF	INTCON,INT0IE		; PS/2受信スタート
	BSF	STATUS,RP0		; ◆ RP=1
	BSF	PIE1,CCP1IE		; リモコン受信スタート
	BSF	PIE1,RCIE
	BCF	STATUS,RP0		; ◆ RP=0
	BSF	STATUS,RP1		; ◆ RP=2
	MOVLW	B'00010111'		; ウォッチドッグタイマスタート 2.048秒
	MOVWF	WDTCON
	BCF	STATUS,RP1		; ◆ RP=0
;------------------------------------------------------------------------------
; メインループ
;------------------------------------------------------------------------------
MAINLOOP_NEXT
	CLRF	IR_STATUS
MAINLOOP
	MOVF	TMR0,W			; 3つのLEDが消えているときにディマーを動作させる
	SUBLW	DIMMER_OFF
	COMF	PORTA,W
	ANDLW	B'00011100'
	MOVLW	BLUE
	BTFSC	CONFIG1,BANKSELECT
	 MOVLW	RED
	BTFSC	FLAGS,DISABLED
	 MOVLW	YELLOW
	BTFSC	FLAGS,TEMPORARILY_DISABLE
	 MOVLW	CYAN
	BTFSS	STATUS,Z
	 GOTO	SET_COLOR
	BTFSS	STATUS,C
	 MOVLW	BLACK
SET_COLOR
	CALL	set_color

	CALL	ps2_check
	BTFSC	FLAGS,REQ_RESET		; PS/2でリセット要求が来たらリセット位置まで飛ばす
	 GOTO	SYSTEM_RESET

	CALL	cmd_editor		; USARTコマンド入力処理
	BTFSC	ED_STATUS,ED_ENTER
	 CALL	cmd_proc

	BTFSC	KD_INCR,0		; 中断されていたキーデータがある場合は再開
	 GOTO	CONTINUE_send_keydata

	MOVLW	RECIEVED		; IR 受信データ処理
	SUBWF	IR_STATUS,W
	BTFSS	STATUS,Z
	 GOTO	MAINLOOP

	CALL	ir_trace
	BTFSS	FLAGS,DISABLED		; IRは受信しているがDISABLEDの時は無視する
	 BTFSC	FLAGS,TEMPORARILY_DISABLE ; オプションバイトの受信待ちの時は一時的に送信を止める
	 GOTO	MAINLOOP_NEXT

	BTFSS	IR_FORMAT,4
	 GOTO	TYPEMATIC_INIT
TYPEMATIC_CHECKDELAY			; IRがリピートだった時、タイプマチックの状態に応じてキーデータを送らない
	MOVF	PS2_DCOUNT,W		; ディレイカウンタが0になって待機が終了しているか？
	BTFSC	STATUS,Z
	 GOTO	TYPEMATIC_CHECKRATE
	DECF	PS2_DCOUNT,F
	GOTO	MAINLOOP_NEXT
TYPEMATIC_CHECKRATE
	DECFSZ	PS2_RCOUNT,F		; 間引きカウンタが終了しているか？
	 GOTO	MAINLOOP_NEXT
	GOTO	TYPEMATIC_RESETRATE
TYPEMATIC_INIT				; IRがデータだった時、ディレイカウンタを初期値にセット
	MOVF	PS2_DELAY,W
	MOVWF	PS2_DCOUNT
TYPEMATIC_RESETRATE			; 間引きカウンタをリセット
	MOVF	PS2_RATE,W
	MOVWF	PS2_RCOUNT

	CALL	search_data
	BTFSC	DATANUMBER,NODATA
	 GOTO	MAINLOOP_NEXT
	
	CALL	prepare_tx_buffer	; マッチしたデータ番号を表示
	MOVLW	'['
	CALL	putchar
	MOVF	DATANUMBER,W
	CALL	decimal8
	MOVLW	LOW txt_matched
	CALL	txstring_relative
CONTINUE_send_keydata
	BTFSS	FLAGS,TEMPORARILY_DISABLE ; オプションバイトの受信待ちの時は一時的に送信を止める
	 CALL	send_keydata
	GOTO	MAINLOOP_NEXT

prepare_tx_buffer
	CALL	usart_tx_sync
	BSF	STATUS,IRP		; ● IRP=1
	MOVLW	LOW TX_BUFFER
	MOVWF	FSR
	RETURN

ps2_check				; ループ中のPS/2共通受信チェック
	CLRWDT
	BTFSC	PS2_STATUS,PS2_RTS	; PS/2 RTS処理
	 CALL	ps2_recieve

	BTFSC	PS2_STATUS,PS2_RECIEVED	; PS/2 受信データ処理
	 CALL	ps2_response
	RETURN
;------------------------------------------------------------------------------
; USARTコマンドラインエディタ
;
; 一時変数
;	TMP23	USARTからFIFO経由で受信した1文字
;------------------------------------------------------------------------------
cmd_editor
	CLRF	ED_STATUS
	MOVF	USEDSIZE,W
	BTFSC	STATUS,Z		; FIFOエンプティチェック
	 RETURN
	CALL	fifo_read
	MOVWF	TMP23

	BSF	STATUS,IRP		; ● IRP=1
	MOVLW	LOW EDITOR_BUFFER	; 文字を保存するアドレスを計算
	ADDWF	ED_CMDLEN,W
	MOVWF	FSR

	MOVLW	BS
	SUBWF	TMP23,W
	BTFSC	STATUS,Z
	 GOTO	BACKSPACE
	
	MOVLW	ETX			; Ctrl+C
	SUBWF	TMP23,W
	BTFSC	STATUS,Z
	 GOTO	CtrlC
	
	MOVLW	CR
	SUBWF	TMP23,W
	BTFSC	STATUS,Z
	 GOTO	COMMIT
STORE1					; それ以外の文字
	MOVF	ED_MAXLEN,W		; バッファサイズに余裕があるなら文字を追加
	SUBWF	ED_CMDLEN,W
	BTFSC	STATUS,Z
	 RETURN
	MOVLW	'A'
	SUBWF	TMP23,W
	BTFSS	STATUS,C
	 GOTO	STORE2
	MOVLW	'Z'+1
	SUBWF	TMP23,W
	BTFSC	STATUS,C
	 GOTO	STORE2
	MOVLW	'a'-'A'			; エディタ上ではアルファベットはすべて小文字として処理する
	ADDWF	TMP23,F
STORE2
	MOVF	TMP23,W			; 保存
	MOVWF	INDF
	INCF	ED_CMDLEN,F

	CALL	prepare_tx_buffer	; 1文字表示
	MOVF	TMP23,W
	CALL	putchar
	CLRF	INDF			; NULL
	GOTO	TXSTART_RETURN

BACKSPACE				; バックスペース
	MOVF	ED_CMDLEN,W		; 1文字以上入力されているならバックスペースを実行
	BTFSC	STATUS,Z
	 RETURN
	DECF	ED_CMDLEN,F
	MOVLW	LOW txt_BS
	GOTO	TXSTRING_RETURN
	
CtrlC					; 入力キャンセル
	MOVLW	LOW EDITOR_BUFFER
	MOVWF	FSR
	CLRF	INDF			; 先頭にNULLを入れてコマンドを消去
	CLRF	ED_CMDLEN
	BSF	ED_STATUS,ED_CANCEL
	MOVLW	LOW txt_DELLINE
	GOTO	TXSTRING_RETURN

COMMIT
	CLRF	INDF			; NULLを入れてコマンドを確定
	CLRF	ED_CMDLEN
	BSF	ED_STATUS,ED_ENTER
	MOVLW	LOW txt_CRLF
	GOTO	TXSTRING_RETURN
;------------------------------------------------------------------------------
; コマンドプロセッサ
;------------------------------------------------------------------------------
cmd_proc
	BSF	STATUS,IRP		; ● IRP=1
	MOVLW	LOW EDITOR_BUFFER
	MOVWF	FSR

	MOVLW	LOW txt_set
	CALL	strcmp
	BTFSC	STATUS,Z
	 GOTO	cmd_ADD

	MOVLW	LOW txt_del
	CALL	strcmp
	BTFSC	STATUS,Z
	 GOTO	cmd_DEL

	MOVLW	LOW txt_list
	CALL	strcmp
	BTFSC	STATUS,Z
	 GOTO	cmd_LIST

	MOVLW	LOW txt_bank
	CALL	strcmp
	BTFSC	STATUS,Z
	 GOTO	cmd_BANK

	MOVLW	LOW txt_init
	CALL	strcmp
	BTFSC	STATUS,Z
	 GOTO	cmd_INIT

	MOVLW	LOW txt_reset
	CALL	strcmp
	BTFSC	STATUS,Z
	 GOTO	cmd_REBOOT

	MOVLW	LOW txt_help
	CALL	strcmp
	BTFSC	STATUS,Z
	 GOTO	cmd_HELP
cmd_proc_END
	MOVLW	LOW txt_prompt
TXSTRING_RETURN
	CALL	txstring
	RETURN
;------------------------------------------------------------------------------
cmd_ADD
	MOVF	KD_INCR,W		; 中断しているキーデータがある場合は実行しない
	BTFSS	STATUS,Z
	 GOTO	cmd_proc_END

	MOVLW	LOW txt_setmsg1		; 登録するIRデータの入力待ち
	CALL	txstring

	MOVLW	44			; 最大15バイト
	MOVWF	ED_MAXLEN
	CLRF	IR_STATUS
cmd_ADD_IRLOOP
	MOVF	TMR0,W
	SUBLW	DIMMER_SET
	MOVLW	GREEN
	BTFSS	STATUS,C
	 MOVLW	BLACK
	CALL	set_color

	CALL	ps2_check
	BTFSC	FLAGS,REQ_RESET		; PS/2でリセット要求が来たらリセット位置まで飛ばす
	 GOTO	SYSTEM_RESET

	CALL	cmd_editor		; USARTコマンド入力処理
	BTFSC	ED_STATUS,ED_CANCEL
	 GOTO	cmd_ADD_cancel
	BTFSC	ED_STATUS,ED_ENTER
	 GOTO	cmd_ADD_fromEDITOR

	MOVLW	RECIEVED		; IR 受信データ処理
	SUBWF	IR_STATUS,W
	BTFSS	STATUS,Z
	 GOTO	cmd_ADD_IRLOOP

	BCF	INTCON,TMR0IE		; リピート無効化タイマーでクリアされるのを防ぐ
	CALL	ir_trace
cmd_ADD_SEARCH_DEST
	CALL	search_data		; 入力されたIRデータから書き込むエリアを導く

	MOVF	IR_LENGTH,W		; 入力データのサイズが0の時(意味不明なリピート等)はもう一度入力
	BTFSC	STATUS,Z
	 GOTO	cmd_ADD
	CALL	bits2bytes		; IRの長さをバイト数に変換して保存
	MOVWF	IR_LENGTH
	SUBLW	15			; 16バイト以上の赤外線データは警告を表示してもう一度入力
	BTFSC	STATUS,C
	 GOTO	cmd_ADD_SEARCH_DEST2
	MOVLW	LOW txt_long
	CALL	txstring
	GOTO	cmd_ADD

cmd_ADD_SEARCH_DEST2
	BTFSC	DATANUMBER,NODATA
	 CALL	search_free		; 同じIRコードが登録されていなければ、先頭から空いている番号を検索
	BTFSC	DATANUMBER,NODATA
	 GOTO	cmd_ADD_full		; 空きエリア無し


	MOVLW	LOW txt_setmsg2		; キーデータ入力の準備
	CALL	getstr
	MOVF	IR_LENGTH,W		; 入力可能バイト数を表示
	SUBLW	16
	CALL	decimal8
	MOVLW	LOW txt_setmsg3
	CALL	txstring_relative

	BCF	STATUS,C		; エディタで編集できる最大文字数を動的に変更する
	RLF	IR_LENGTH,W
	ADDWF	IR_LENGTH,W
	SUBLW	47
	MOVWF	ED_MAXLEN

	MOVLW	GREEN
	CALL	set_color
cmd_ADD_EDITORLOOP
	CALL	ps2_check
	BTFSC	FLAGS,REQ_RESET		; PS/2でリセット要求が来たらリセット位置まで飛ばす
	 GOTO	SYSTEM_RESET

	CALL	cmd_editor		; USARTコマンド入力処理
	BTFSC	ED_STATUS,ED_CANCEL
	 GOTO	cmd_ADD_cancel
	BTFSS	ED_STATUS,ED_ENTER
	 GOTO	cmd_ADD_EDITORLOOP

	MOVF	DATANUMBER,W		; 書き込み先を削除
	CALL	delete_data

	CLRF	TMP23
cmd_ADD_IRGEN_LOOP
	MOVF	TMP23,W
	ADDLW	LOW IR_BUFFER		; IRバッファからフラッシュワークエリアへコピー
	MOVWF	FSR
	MOVF	INDF,W
	MOVWF	TMP22

	BCF	STATUS,C
	RLF	TMP23,W
	ADDLW	FLASH_WORK
	MOVWF	FSR
	MOVF	TMP22,W
	CALL	putchar			; LSB 通常データ
	CLRF	INDF			; MSB

	INCF	TMP23,F
	MOVF	TMP23,W
	SUBWF	IR_LENGTH,W
	BTFSS	STATUS,Z
	 GOTO	cmd_ADD_IRGEN_LOOP
	 
	MOVLW	FLASH_WORK+1		; MSBにバイト数を保存
	MOVWF	FSR
	MOVF	IR_LENGTH,W
	MOVWF	INDF


	MOVLW	LOW EDITOR_BUFFER
	MOVWF	FSR
	CLRF	TMP00			; バイナリ変換後の入力バイト数
cmd_ADD_KEYGEN_LOOP
	MOVF	TMP00,W
	ADDWF	IR_LENGTH,W
	SUBLW	15
	BTFSS	STATUS,C		; IRデータとキーデータの合計が16バイト以上になったらそれ以上は保存しない
	 GOTO	cmd_ADD_FILL3FFF

	BSF	STATUS,IRP		; ● IRP=1
	CALL	hex8bin
	MOVWF	TMP01
	BTFSC	FLAGS,RESULT
	 GOTO	cmd_ADD_FILL3FFF	; 変換できない文字が入っている場合、データ終了とする
	MOVF	FSR,W			; FSRを退避
	MOVWF	TMP02

	BCF	STATUS,IRP		; ● IRP=0
	MOVF	IR_LENGTH,W
	ADDWF	TMP00,W
	MOVWF	TMP03
	BCF	STATUS,C
	RLF	TMP03,W
	ADDLW	FLASH_WORK
	MOVWF	FSR

	MOVF	TMP01,W
	CALL	putchar			; LSB
	CLRF	INDF			; MSB

	MOVF	TMP02,W			; コマンド文字列ポインタを復帰
	MOVWF	FSR
	INCF	TMP00,F
	GOTO	cmd_ADD_KEYGEN_LOOP
cmd_ADD_FILL3FFF
	MOVF	TMP00,W			; 使われていないエリアを3FFFhでパディングする
	ADDWF	IR_LENGTH,W
	SUBLW	17
	MOVWF	TMP01
	BCF	STATUS,IRP		; ● IRP=0
cmd_ADD_FILLLOOP
	DECF	TMP01,F
	BTFSC	STATUS,Z
	 GOTO	cmd_ADD_STORE
	BCF	STATUS,C
	RLF	TMP01,W
	SUBLW	FLASH_WORK+32
	MOVWF	FSR
	MOVLW	0xFF
	CALL	putchar
	MOVWF	INDF
	GOTO	cmd_ADD_FILLLOOP
cmd_ADD_STORE
	BCF	STATUS,C
	RLF	IR_LENGTH,W
	ADDLW	FLASH_WORK+1		; キーデータの長さを保存
	MOVWF	FSR
	MOVF	TMP00,W
	MOVWF	INDF
	
	MOVLW	FLASH_WORK
	MOVWF	FSR
	MOVF	DATANUMBER,W		; 書き込み先アドレスを計算
	MOVWF	TMP00
	SWAPF	TMP00,W			; データの開始アドレス
	ANDLW	0xF0
	MOVWF	TMP20
	SWAPF	TMP00,W
	ANDLW	15
	ADDLW	HIGH KEYDATA
	MOVWF	TMP21
	CALL	write16_flash		; 生成したデータを書き込み

	CALL	prepare_tx_buffer
	MOVLW	'['
	CALL	putchar
	MOVF	DATANUMBER,W
	CALL	decimal8
	MOVLW	LOW txt_stored
	CALL	txstring_relative
cmd_ADD_END
	CLRF	IR_LENGTH
	CLRF	IR_STATUS		; 未処理のリモコンコードが残る事があるので強制クリア
	MOVLW	EDITOR_BUFFER_SIZE-1
	MOVWF	ED_MAXLEN
	GOTO	cmd_proc_END

cmd_ADD_full
	MOVLW	LOW txt_full		; メモリフルメッセージ
cmd_ADD_TXEND	
	CALL	txstring
	GOTO	cmd_ADD_END

cmd_ADD_cancel
	MOVLW	LOW txt_canceled	; キャンセルメッセージ
	GOTO	cmd_ADD_TXEND

cmd_ADD_fromEDITOR			; エディタでリモコンコードを入力する場合、入力した値を赤外線バッファに書き込む
	MOVLW	RECIEVED		; 赤外線バッファをロック
	MOVWF	IR_STATUS
	MOVLW	LOW EDITOR_BUFFER
	MOVWF	FSR
	CLRF	TMP00			; バイナリ変換後の入力バイト数
cmd_ADD_fromEDITOR_LOOP
	MOVF	TMP00,W
	SUBLW	15
	BTFSS	STATUS,C		; 入力データが16バイト以上になったらそれ以上は保存しない
	 GOTO	cmd_ADD_fromEDITOR_FINISH

	BSF	STATUS,IRP		; ● IRP=1
	CALL	hex8bin
	MOVWF	TMP01
	BTFSC	FLAGS,RESULT
	 GOTO	cmd_ADD_fromEDITOR_FINISH ; 変換できない文字が入っている場合、データ終了とする
	MOVF	FSR,W			; FSRを退避
	MOVWF	TMP02

	BCF	STATUS,IRP		; ● IRP=0
	MOVF	TMP00,W
	ADDLW	LOW IR_BUFFER
	MOVWF	FSR
	MOVF	TMP01,W
	MOVWF	INDF

	MOVF	TMP02,W			; コマンド文字列ポインタを復帰
	MOVWF	FSR
	INCF	TMP00,F
	GOTO	cmd_ADD_fromEDITOR_LOOP
cmd_ADD_fromEDITOR_FINISH
	RLF	TMP00,F
	RLF	TMP00,F
	RLF	TMP00,W
	ANDLW	B'01111000'
	MOVWF	IR_LENGTH
	GOTO	cmd_ADD_SEARCH_DEST	; 赤外線で入力された直後へ合流
;------------------------------------------------------------------------------
cmd_DEL
	CALL	dec8bin
	MOVWF	TMP00
	BTFSC	FLAGS,RESULT		; 値が読み込めなければキャンセルする
	 GOTO	cmd_DEL_cancel

	MOVLW	0x80			; 128以上の不正な値はキャンセルする
	SUBWF	TMP00,W
	BTFSC	STATUS,C
	 GOTO	cmd_proc_END

	MOVF	TMP00,W
	CALL	delete_data

	CALL	prepare_tx_buffer
	MOVLW	'['
	CALL	putchar
	MOVF	TMP00,W
	CALL	decimal8
	MOVLW	LOW txt_deleted
	CALL	txstring_relative
	GOTO	cmd_proc_END
cmd_DEL_cancel
	MOVLW	LOW txt_canceled
	CALL	txstring
	GOTO	cmd_proc_END
;------------------------------------------------------------------------------
cmd_LIST
	CALL	datalist
	GOTO	cmd_proc_END
;------------------------------------------------------------------------------
cmd_BANK
	CALL	bankchange
	GOTO	cmd_proc_END
;------------------------------------------------------------------------------
cmd_INIT
	CALL	init_flash
	MOVLW	LOW txt_inited
	CALL	txstring
	GOTO	cmd_proc_END
;------------------------------------------------------------------------------
cmd_REBOOT
	BSF	FLAGS,REQ_RESET
	GOTO	cmd_proc_END
;------------------------------------------------------------------------------
cmd_HELP
	MOVLW	LOW txt_title
	CALL	txstring
	GOTO	cmd_proc_END
;------------------------------------------------------------------------------
; PS/2 レスポンダ
;
; 引数		PS2_RXDATA	ホストから送られてきたコマンド
;------------------------------------------------------------------------------
ps2_response
	BTFSS	PS2_RXDATA,7		; 0x80以下はオプションバイト受信
	 GOTO	PS2CMD_OPTDATA
	MOVLW	HIGH PS2_CMD
	MOVWF	PCLATH			; ■ PCLATH
	MOVLW	0xED
	SUBWF	PS2_RXDATA,W
	BTFSS	STATUS,C
	 GOTO	PS2CMD_INVALID		; * = Not support
PS2_CMD	ADDWF	PCL,F			; + = Require option byte
	GOTO	PS2CMD_REQOPTION	; + Set/Reset LEDs
	GOTO	PS2CMD_EE		;   Echo Request
	GOTO	PS2CMD_INVALID		;   Invalid
	GOTO	PS2CMD_REQOPTION	; + Set Scan Code Set
	GOTO	PS2CMD_INVALID		;   Invalid
	GOTO	PS2CMD_F2		;   Read ID
	GOTO	PS2CMD_REQOPTION	; + Set Typematic Rate/Delay
	GOTO	PS2CMD_F4		;   Enable
	GOTO	PS2CMD_F5		;   Default Disable
	GOTO	PS2CMD_F6		;   Set Default
	GOTO	PS2CMD_INVALID		; * Set All Keys Typematic
	GOTO	PS2CMD_INVALID		; * Set All Keys Make/Break
	GOTO	PS2CMD_INVALID		; * Set All Keys Make
	GOTO	PS2CMD_INVALID		; * Set All Keys Typematic/Make/Break
	GOTO	PS2CMD_INVALID		; * Set Key Type Typematic
	GOTO	PS2CMD_INVALID		; * Set Key Type Make/Break
	GOTO	PS2CMD_INVALID		; * Set Key Type Make
	GOTO	PS2CMD_FE		;   Resend
	GOTO	PS2CMD_FF		;   Reset
;------------------------------------------------------------------------------
PS2CMD_REQOPTION			; オプションバイトが必要なコマンドの共通動作
	CALL	acknowledge
	BSF	FLAGS,TEMPORARILY_DISABLE ; 一時的にスキャンを停止
	MOVF	PS2_RXDATA,W
	MOVWF	PS2_LASTCMD
	GOTO	PS2_RESPONSE_DISABLED_END
;------------------------------------------------------------------------------
PS2CMD_OPTDATA				; オプションバイト受信時の分岐
	MOVLW	0xF0
	SUBWF	PS2_LASTCMD,W
	BTFSC	STATUS,Z
	 GOTO	PS2CMD_F0

	CALL	acknowledge

	MOVLW	0xED
	SUBWF	PS2_LASTCMD,W
	BTFSC	STATUS,Z
	 GOTO	PS2CMD_ED

	MOVLW	0xF3
	SUBWF	PS2_LASTCMD,W
	BTFSC	STATUS,Z
	 GOTO	PS2CMD_F3
	GOTO	PS2CMD_INVALID		; 不正なパラメータはRESENDすると初めのコマンドも同時に送られてくるらしい
;------------------------------------------------------------------------------
PS2CMD_ED				; Set/Reset LEDs
	BTFSC	PS2_RXDATA,0		; LEDの物理配列に加工
	 BSF	PS2_RXDATA,3
	COMF	PS2_RXDATA,F
	RLF	PS2_RXDATA,W
	ANDLW	B'00011100'
	MOVWF	TMP00
	MOVF	PORTA,W
	ANDLW	B'11100011'
	IORWF	TMP00,W
	MOVWF	PORTA
	GOTO	PS2_RESPONSE_END
;------------------------------------------------------------------------------
PS2CMD_EE				; Echo Request
	MOVLW	0xEE
	GOTO	PS2_RESPONSE
;------------------------------------------------------------------------------
PS2CMD_F0				; Set Scan Code Set
	CLRF	KD_INCR			; 出力バッファクリア(送信中断を解除)
	BTFSC	PS2_RXDATA,0
	 GOTO	PS2CMD_INVALID		; 1および3のコードセットはサポートしない(RESEND)

	CALL	acknowledge		; 0,2の時、ACKを返す
	MOVF	PS2_RXDATA,W
	MOVLW	2
	BTFSC	STATUS,Z		; 0の時、現在のコードセット2を返す
	 GOTO	PS2_RESPONSE
	GOTO	PS2_RESPONSE_END
;------------------------------------------------------------------------------
PS2CMD_F2				; Read ID
	CALL	acknowledge
	MOVLW	0xAB
	CALL	PS2CMD_send_force
	MOVLW	0x83
	GOTO	PS2_RESPONSE
;------------------------------------------------------------------------------
PS2CMD_F3				; Set Typematic Rate/Delay
	SWAPF	PS2_RXDATA,W		; ディレイの間引き数を計算
	ANDLW	6
	ADDLW	2
	MOVWF	PS2_DELAY

	MOVLW	1			; レートの間引き数を計算
	MOVWF	PS2_RATE
	BTFSS	PS2_RXDATA,4
	 GOTO	PS2_RESPONSE_END
	INCF	PS2_RATE,F
	BTFSS	PS2_RXDATA,3
	 GOTO	PS2_RESPONSE_END
	INCF	PS2_RATE,F
	BTFSC	PS2_RXDATA,2
	 INCF	PS2_RATE,F
	GOTO	PS2_RESPONSE_END
;------------------------------------------------------------------------------
PS2CMD_F4				; Enable
	CALL	acknowledge
	CLRF	KD_INCR			; 出力バッファクリア(送信中断を解除)
	BCF	FLAGS,DISABLED
	GOTO	PS2_RESPONSE_END
;------------------------------------------------------------------------------
PS2CMD_F5				; Default Disable
	BSF	FLAGS,DISABLED
PS2CMD_F6				; Set Default
	CALL	acknowledge
	CLRF	KD_INCR			; 出力バッファクリア(送信中断を解除)
	CALL	set_default
	GOTO	PS2_RESPONSE_END
;------------------------------------------------------------------------------
PS2CMD_FE				; Resend
	DECFSZ	PS2_RESENDCOUNT,F
	 GOTO	PS2CMD_FE_DORESEND
	CLRF	KD_INCR			; 再送累計が上限を超えたらバッファをクリアする。
	MOVLW	RESENDMAX		; 単に再送を無視して無限ループを防ぐ場合。
	MOVWF	PS2_RESENDCOUNT
;	BSF	FLAGS,DISABLED		; DISABLEDに入れて以降の動作を禁止する場合。
	GOTO	PS2_RESPONSE_END
PS2CMD_FE_DORESEND
	MOVF	PS2_TXLAST,W
	GOTO	PS2_RESPONSE
;------------------------------------------------------------------------------
PS2CMD_FF				; Reset
	CALL	acknowledge
	BSF	FLAGS,REQ_RESET
	CALL	usart_tx_sync
	GOTO	PS2_RESPONSE_END
;------------------------------------------------------------------------------
PS2CMD_INVALID
	MOVLW	0xFE			; RESEND
PS2_RESPONSE
	CALL	PS2CMD_send_force
PS2_RESPONSE_END
	BCF	FLAGS,TEMPORARILY_DISABLE
PS2_RESPONSE_DISABLED_END
	BCF	PS2_STATUS,PS2_RECIEVED
	RETURN
;------------------------------------------------------------------------------
acknowledge
	MOVLW	0xFA			; ACK
PS2CMD_send_force			; ACKは例外的に送信中に中断されても、ACKを破棄して次の動作に進んでも良い。
	MOVWF	PS2_TXDATA		; ここでは共通化のために次へ進まず送信待ちで無限ループする。
	CALL	ps2_send
	BTFSS	PS2_STATUS,PS2_TRANSMITTED
	 GOTO	$-2
	RETURN
;------------------------------------------------------------------------------
set_default
	MOVLW	DEFAULT_DELAY
	MOVWF	PS2_DELAY
	MOVLW	DEFAULT_RATE
	MOVWF	PS2_RATE
	RETURN
;------------------------------------------------------------------------------
; PS/2送信
;
; 引数		PS2_TXDATA	送信データ(非破壊)
; 戻り値	PS2_STATUS	送信ステータス
;		PS2_TXLAST	最終送信完了データ
; 注意		送信エラーで戻った場合、複数バイトのストロークは初めから再送しなければいけない。
;------------------------------------------------------------------------------
ps2_send
	BCF	INTCON,INT0IE		; クロック割り込み禁止
	MOVLW	B'01001111'
	ANDWF	PS2_STATUS,F		; (TMTD,INH,RTS)=0

	BTFSS	PORTB,SCLK		; 送信禁止チェック
	 GOTO	PS2_SEND_INHIBIT
	BTFSS	PORTB,SDATA		; 送信要求チェック
	 GOTO	PS2_SEND_RTS
	
	MOVF	PS2_TXDATA,W
	MOVWF	TMP00			; データシフト用
	CLRF	TMP01			; パリティ計算用

	BSF	STATUS,RP0		; ◆ RP=1
	BCF	TRISB,SDATA		; スタートビット
	CALL	ps2_sendclock

	MOVLW	8
	MOVWF	TMP02
PS2_SENDLOOP				; データビットループ
	BCF	STATUS,RP0		; ◆ RP=0
	BTFSS	PORTB,SCLK		; 送信禁止チェック
	 GOTO	PS2_SEND_INHIBIT

	RRF	TMP00,F
	BSF	STATUS,RP0		; ◆ RP=1
	BTFSS	STATUS,C		; 0
	 BCF	TRISB,SDATA
	BTFSC	STATUS,C		; 1
	 BSF	TRISB,SDATA
	BTFSC	STATUS,C		; 1:パリティ計算
	 INCF	TMP01,F
	CALL	ps2_sendclock
	DECFSZ	TMP02,F
	 GOTO	PS2_SENDLOOP
	
	BCF	STATUS,RP0		; ◆ RP=0
	BTFSS	PORTB,SCLK		; 送信禁止チェック
	 GOTO	PS2_SEND_INHIBIT

	RRF	TMP01,W
	BSF	STATUS,RP0		; ◆ RP=1
	BTFSS	STATUS,C		; パリティビット
	 BSF	TRISB,SDATA
	BTFSC	STATUS,C
	 BCF	TRISB,SDATA
	CALL	ps2_sendclock
	 
	BCF	STATUS,RP0		; ◆ RP=0
	BTFSS	PORTB,SCLK		; 送信禁止チェック
	 GOTO	PS2_SEND_INHIBIT

	BSF	STATUS,RP0		; ◆ RP=1
	BSF	TRISB,SDATA		; ストップビット
	CALL	ps2_sendclock
	MOVLW	30
	CALL	ps2clockwait

	BCF	STATUS,RP0		; ◆ RP=0
	BSF	PS2_STATUS,PS2_TRANSMITTED
	MOVF	PS2_TXDATA,W
	SUBLW	0xFE
	MOVF	PS2_TXDATA,W		; 正常に送信出来たら最終送信データとして保存
	BTFSS	STATUS,Z		; ただしRESEND(FE)の時は保存しない
	 MOVWF	PS2_TXLAST
PS2_SEND_RETURN
	BSF	STATUS,RP0		; ◆ RP=1
	BSF	TRISB,SDATA		; INHとRTSの時はSDATAがLになっている事があるので戻す
	BCF	STATUS,RP0		; ◆ RP=0

	CALL	prepare_tx_buffer	; トレース用
	MOVLW	's'
	CALL	putchar
	MOVF	PS2_TXDATA,W
	CALL	ps2_trace

	BCF	INTCON,INT0IF
	BSF	INTCON,INT0IE		; クロック割り込み許可
	BTFSC	PS2_STATUS,PS2_INHIBIT	; inhibitなら勝手にもう一度送る
	 GOTO	ps2_send
	RETURN

PS2_SEND_INHIBIT
	BSF	PS2_STATUS,PS2_INHIBIT
	GOTO	PS2_SEND_RETURN

PS2_SEND_RTS
	BSF	PS2_STATUS,PS2_RTS
	GOTO	PS2_SEND_RETURN

ps2_sendclock
	MOVLW	30
	CALL	ps2clockwait
	BCF	TRISB,SCLK
	MOVLW	60
	CALL	ps2clockwait
	BSF	TRISB,SCLK
	MOVLW	30
	CALL	ps2clockwait
	RETURN	

ps2clockwait
	MOVWF	TMP03
PS2CLOCK_LOOP
	DECFSZ	TMP03,F
	 GOTO	PS2CLOCK_LOOP
	RETURN
;------------------------------------------------------------------------------
; PS/2受信
;
; 戻り値	PS2_RXDATA	受信データ
; 		PS2_STATUS	受信ステータス
;------------------------------------------------------------------------------
ps2_recieve
	BCF	INTCON,INT0IE		; クロック割り込み禁止
	MOVLW	B'11000100'		; (INH,RTS,RCVD,FERR,PERR) = 0
	ANDWF	PS2_STATUS,F
	CLRF	PS2_RXDATA

	CLRF	TMP01			; パリティ計算用
	MOVLW	8
	MOVWF	TMP02			; ビットカウンタ
PS2_RXLOOP				; データビットループ
	BCF	STATUS,RP0		; ◆ RP=0
	BTFSS	PORTB,SCLK		; 送信禁止チェック
	 GOTO	PS2_RECIEVE_INHIBIT

 	BSF	STATUS,RP0		; ◆ RP=1
	BCF	TRISB,SCLK
	MOVLW	60
	CALL	ps2clockwait
	BSF	TRISB,SCLK
	BCF	STATUS,RP0		; ◆ RP=0
	BTFSS	PORTB,SDATA
	 BCF	STATUS,C		; 0
	BTFSC	PORTB,SDATA
	 BSF	STATUS,C		; 1
	BTFSC	PORTB,SDATA
	 INCF	TMP01,F			; 1:パリティ計算
	RRF	PS2_RXDATA,F
	MOVLW	60
	CALL	ps2clockwait
	DECFSZ	TMP02,F
	 GOTO	PS2_RXLOOP

	BTFSS	PORTB,SCLK		; 送信禁止チェック
	 GOTO	PS2_RECIEVE_INHIBIT
	BSF	STATUS,RP0		; ◆ RP=1
	BCF	TRISB,SCLK		; パリティビット
	MOVLW	60
	CALL	ps2clockwait
	BSF	TRISB,SCLK
	BCF	STATUS,RP0		; ◆ RP=0
	BTFSC	PORTB,SDATA
	 INCF	TMP01,F			; パリティチェック
	MOVLW	60
	CALL	ps2clockwait
	BTFSS	TMP01,0
	 BSF	PS2_STATUS,PS2_PERR

	BTFSS	PORTB,SCLK		; 送信禁止チェック
	 GOTO	PS2_RECIEVE_INHIBIT
	BSF	STATUS,RP0		; ◆ RP=1
	BCF	TRISB,SCLK		; ストップビット
	MOVLW	60
	CALL	ps2clockwait
	BSF	TRISB,SCLK
	BCF	STATUS,RP0		; ◆ RP=0
	BTFSS	PORTB,SDATA
	 GOTO	PS2_RECIEVE_FERR
	MOVLW	30
	CALL	ps2clockwait

	BTFSS	PORTB,SCLK		; 送信禁止チェック
	 GOTO	PS2_RECIEVE_INHIBIT
	BSF	STATUS,RP0		; ◆ RP=1
	BCF	TRISB,SDATA		; ACKビット
	CALL	ps2_sendclock
	BSF	TRISB,SDATA
	MOVLW	30
	CALL	ps2clockwait

	BCF	STATUS,RP0		; ◆ RP=0
	BTFSS	PS2_STATUS,PS2_PERR	; パリティエラーの時は受信完了状態にしない
	 BSF	PS2_STATUS,PS2_RECIEVED
PS2_RECIEVE_RETURN
	BSF	STATUS,RP0		; ◆ RP=1
	BSF	TRISB,SDATA		; INHとRTSの時はSDATAがLになっている事があるので戻す
	BCF	STATUS,RP0		; ◆ RP=0

	CALL	prepare_tx_buffer	; トレース用
	MOVLW	'r'
	CALL	putchar
	MOVF	PS2_RXDATA,W
	CALL	ps2_trace

	BCF	INTCON,INT0IF
	BSF	INTCON,INT0IE		; クロック割り込み許可
	MOVLW	0xFE			; 正常に受信できていない時はRESENDを自動で送る
	BTFSS	PS2_STATUS,PS2_RECIEVED
	 CALL	PS2CMD_send_force
	RETURN

PS2_RECIEVE_INHIBIT
	BSF	PS2_STATUS,PS2_INHIBIT
	GOTO	PS2_RECIEVE_RETURN

PS2_RECIEVE_FERR			; フレーミングエラーリカバリ
	BSF	PS2_STATUS,PS2_FERR
	MOVLW	60
	CALL	ps2clockwait
	MOVLW	CYAN
	CALL	set_color
PS2_RECIEVE_FERR_RECOVERY
	BSF	STATUS,RP0		; ◆ RP=1
	BCF	TRISB,SCLK
	MOVLW	60
	CALL	ps2clockwait
	BSF	TRISB,SCLK
	MOVLW	60
	CALL	ps2clockwait
	BCF	STATUS,RP0		; ◆ RP=0
	BTFSS	PORTB,SDATA		; ホストからSDATAが開放されない限り無限ループする
	 GOTO	PS2_RECIEVE_FERR_RECOVERY
	GOTO	PS2_RECIEVE_RETURN
;------------------------------------------------------------------------------
ps2_trace
	CALL	bin2hex
	MOVLW	' '
	CALL	putchar
	MOVF	PS2_STATUS,W
	CALL	bin2hex
	MOVLW	CR
	CALL	putchar
	MOVLW	LF
	CALL	putchar
	CLRF	INDF
	GOTO	TXSTART_RETURN
;------------------------------------------------------------------------------
; IRデータバッファを表示
;------------------------------------------------------------------------------
ir_trace
	MOVF	IR_FORMAT,W		; フォーマット
	ANDLW	3
	MOVWF	TMP23
	
	MOVLW	LOW txt_CRLF
	DECF	TMP23,F
	BTFSC	STATUS,Z
	 MOVLW	LOW txt_AEHA
	DECF	TMP23,F
	BTFSC	STATUS,Z
	 MOVLW	LOW txt_NEC
	DECF	TMP23,F
	BTFSC	STATUS,Z
	 MOVLW	LOW txt_SONY
	MOVWF	TMP20
	CALL	getstr

	MOVLW	LOW txt_REPEAT
	BTFSC	IR_FORMAT,4		; リピート
	 CALL	getstr_relative
ir_trace_BITS
	MOVF	IR_LENGTH,W		; ビット数
	CALL	decimal8
	MOVLW	LOW txt_bits
	CALL	getstr_relative

	MOVLW	LOW IR_BUFFER		; ループ用にバイト数に変換
	MOVWF	TMP00
	MOVF	IR_LENGTH,W
	CALL	bits2bytes
	MOVWF	TMP03			; TMP03 ループカウンタ
	BTFSC	STATUS,Z		; 0バイトの時は実行しない
	 GOTO	IR_DISPLAY
IR_DATAS
	BCF	STATUS,IRP		; ● IRP=0
	MOVF	FSR,W			; TMP01 TX_BUFFERのポインタ
	MOVWF	TMP01
	MOVF	TMP00,W			; TMP00 IR_BUFFERのポインタ
	MOVWF	FSR
	MOVF	INDF,W			; TMP02 IRデータ
	MOVWF	TMP02

	BSF	STATUS,IRP		; ● IRP=1
	MOVF	TMP01,W
	MOVWF	FSR
	MOVF	TMP02,W
	CALL	bin2hex
	MOVLW	' '
	CALL	putchar
	INCF	TMP00,F
	DECFSZ	TMP03,F
	 GOTO	IR_DATAS
IR_DISPLAY
	MOVLW	LOW txt_CRLF
	CALL	txstring_relative
	RETURN
;------------------------------------------------------------------------------
; ビット数をバイト数に変換、端数ビットは切り上げられる
;
; 引数
;	W		ビット数
;
; 戻り値
;	W		バイト数
;
; 使用変数
;	MATH_TEMP
;------------------------------------------------------------------------------
bits2bytes
	ADDLW	7
	MOVWF	MATH_TEMP
	RRF	MATH_TEMP,F
	RRF	MATH_TEMP,F
	RRF	MATH_TEMP,W
	ANDLW	B'00011111'
	RETURN
;------------------------------------------------------------------------------
; IRデータ検索
;
; 引数
;	IR_LENGTH
;	IR_BUFFER
;	BANKSELECT
;
; 戻り値
;	KD_ADDRx	キーデータの開始アドレス
;	DATANUMBER	データ番号
;
; 使用変数
;	TMP10,11,20,21,22,23
;------------------------------------------------------------------------------
search_data
	BTFSC	IR_FORMAT,4		; リピートの時は前回検索したデータ番号を更新しない
	 RETURN
	
	MOVF	IR_LENGTH,W		; 受信ビット数をバイト数に変換
	CALL	bits2bytes
	MOVWF	TMP00
	BTFSC	STATUS,Z		; 受信サイズが0なら即リターン
	 GOTO	search_data_NOTFOUND

	CLRF	TMP20
	MOVLW	HIGH KEYDATA
	BTFSC	CONFIG1,BANKSELECT
	 ADDLW	4
	MOVWF	TMP21
	MOVLW	64
	MOVWF	TMP10
	BCF	STATUS,IRP		; ● IRP=0
search_length_LOOP
	CALL	read_flash
	MOVF	TMP23,W			; 受信したデータの長さを比較
	SUBWF	TMP00,W
	BTFSC	STATUS,Z
	 GOTO	search_cmpdata		; 長さが一致した
search_length_NEXT
	MOVLW	16			; 長さが一致しなかった
	ADDWF	TMP20,W
	ANDLW	256-16
	MOVWF	TMP20
	BTFSC	STATUS,C
	 INCF	TMP21,F
	DECFSZ	TMP10,F
	 GOTO	search_length_LOOP
search_data_NOTFOUND
	BSF	DATANUMBER,NODATA	; どれも長さが一致しなかった
	RETURN

search_cmpdata
	MOVLW	LOW IR_BUFFER
	MOVWF	FSR
	MOVF	TMP23,W
	MOVWF	TMP11
search_cmpdata_LOOP
	CALL	read_flash		; 受信したデータを比較
	SUBWF	INDF,W
	BTFSS	STATUS,Z
	 GOTO	search_length_NEXT	; データが一致しなかった
search_cmpdata_NEXT
	INCF	FSR,F			; データが一致した
	INCF	TMP20,F
	BTFSC	STATUS,Z
	 INCF	TMP21,F
	DECFSZ	TMP11,F
	 GOTO	search_cmpdata_LOOP
search_data_FOUND
	MOVF	TMP20,W
	MOVWF	KD_ADDRL
	MOVF	TMP21,W
	MOVWF	KD_ADDRH
search_free_FOUND			; DATANUMBER生成式が共通なのでここへ飛ばす
	SWAPF	TMP20,W
	ANDLW	15
	MOVWF	DATANUMBER
	SWAPF	TMP21,W
	ANDLW	0xF0
	ADDLW	0x80
	IORWF	DATANUMBER,F
	RETURN
;------------------------------------------------------------------------------
; 指定のバンクで保存用の空き領域を検索
;
; 引数
;	BANKSELECT
;
; 戻り値
;	TMP20		キーデータのアドレスLSB
;	TMP21		キーデータのアドレスMSB
;	DATANUMBER	データ番号
;
; 使用変数
;	TMP10,20,21,22,23
;------------------------------------------------------------------------------
search_free
	CLRF	TMP20
	MOVLW	HIGH KEYDATA
	BTFSC	CONFIG1,BANKSELECT
	 ADDLW	4
	MOVWF	TMP21
	MOVLW	64
	MOVWF	TMP10
search_free_LOOP
	CALL	read_flash
	MOVF	TMP23,W
	BTFSC	STATUS,Z
	 GOTO	search_free_FOUND
	SUBLW	15			; IRのデータ長が0または16以上なら空き領域とみなす
	BTFSS	STATUS,C
	 GOTO	search_free_FOUND
search_free_NEXT
	MOVLW	16
	ADDWF	TMP20,W
	ANDLW	256-16
	MOVWF	TMP20
	BTFSC	STATUS,C
	 INCF	TMP21,F
	DECFSZ	TMP10,F
	 GOTO	search_free_LOOP
search_free_NOTFOUND
	BSF	DATANUMBER,NODATA
	RETURN
;------------------------------------------------------------------------------
; キーデータ送信
;   送信バッファを持たないため、KD_INCRが0か否かで継続データの有無を判別する。
;
; 引数
;	KD_ADDRx	キーデータの送信開始アドレス
;	KD_INCR		キーデータ読み出し方向、0の時は送信が完了している
;	KD_SIZE		送信すべきキーコードの数(再開時に正しいデータが入っていないといけない)
;
; 使用変数
;	TMP20,21,22,23
;------------------------------------------------------------------------------
send_keydata
	MOVLW	BLACK			; 送信中は黒
	CALL	set_color

	MOVF	KD_ADDRL,W
	MOVWF	TMP20
	MOVF	KD_ADDRH,W
	MOVWF	TMP21
	MOVF	KD_INCR,W		; KD_INCRが0以外の時は再開扱いでループを回す
	BTFSS	STATUS,Z
	 GOTO	send_keydata_CONTINUE

	CALL	read_flash		; 送信するキーデータのサイズを取得
	MOVF	TMP23,W
	MOVWF	KD_SIZE
	BTFSC	STATUS,Z		; 送信サイズが0または16以上ならデータ無しとする
	 RETURN
	SUBLW	15
	BTFSS	STATUS,C
	 RETURN

	MOVF	TMP22,W			; キーデータの1バイト目が0ならバンクチェンジへ強制移動
	BTFSC	STATUS,Z
	 GOTO	bankchange

	MOVF	KD_SIZE,W
	MOVWF	KD_COUNT		; ループカウンタセット
	MOVLW	1
	MOVWF	KD_INCR			; アドレス加算を+1に設定
send_keydata_LOOP
	CALL	read_flash		; キーコード取得
	MOVWF	KD_KEYCODE
	BTFSS	STATUS,Z		; 0ならワンショット用にデータ番号を無効化して次回使えなくする
	 GOTO	send_keydata_TEST90
	BSF	DATANUMBER,NODATA
	GOTO	send_keydata_LOOP_NEXT
send_keydata_TEST90
	MOVLW	0x90			; キーコード0x90以上はE0プレフィックスを送信、キーコードを-0x80する
	SUBWF	KD_KEYCODE,W
	BTFSS	STATUS,C
	 GOTO	send_keydata_ISBREAK
	MOVLW	0xE0
	MOVWF	PS2_TXDATA
	CALL	ps2_send
	BTFSC	PS2_STATUS,PS2_RTS	; RTSなら現在送信中のアドレスを保存して中断
 	 GOTO	send_keydata_SAVECONTEXT
	MOVLW	0x80
	SUBWF	KD_KEYCODE,F
send_keydata_ISBREAK
	INCF	KD_INCR,W		; 戻り(BREAK)フェーズの時、F0プレフィックスを送信
	BTFSS	STATUS,Z
	 GOTO	send_keydata_SEND
	MOVLW	0xF0
	MOVWF	PS2_TXDATA
	CALL	ps2_send
	BTFSC	PS2_STATUS,PS2_RTS	; RTSなら現在送信中のアドレスを保存して中断
 	 GOTO	send_keydata_SAVECONTEXT
send_keydata_SEND
	MOVF	KD_KEYCODE,W		; スキャンコード本体を送信
	MOVWF	PS2_TXDATA
	CALL	ps2_send
	BTFSC	PS2_STATUS,PS2_RTS	; RTSなら現在送信中のアドレスを保存して中断
	 GOTO	send_keydata_SAVECONTEXT
send_keydata_LOOP_NEXT
	MOVF	KD_INCR,W		; アドレスをINCR/DECRする
	ADDWF	TMP20,F			; 16バイト境界をまたがないようデータを作るので、上位TMP21の繰り上がり処理は不要
	DECFSZ	KD_COUNT,F
	 GOTO	send_keydata_LOOP

	INCF	KD_INCR,W		; 戻り(BREAK)フェーズなら送信完了
	BTFSC	STATUS,Z
	 GOTO	send_keydata_COMPLETE

	MOVF	KD_SIZE,W		; 戻りフェーズのループカウンタセット
	MOVWF	KD_COUNT
	MOVLW	0xFF			; アドレスデクリメントに切り替え
	MOVWF	KD_INCR
	DECF	TMP20,F			; インクリメントされてしまったアドレスを戻す
	GOTO	send_keydata_LOOP

send_keydata_COMPLETE
	CLRF	KD_INCR			; 送信完了なのでKD_INCRをクリアする
	RETURN

send_keydata_SAVECONTEXT
	MOVF	TMP20,W			; 汎用変数から専用変数へ戻す
	MOVWF	KD_ADDRL
	MOVF	TMP21,W
	MOVWF	KD_ADDRH
	RETURN

send_keydata_CONTINUE
	CLRF	PS2_TXDATA		; E0h/E1hの重複エラーを回避するため、再開前に00hを送る。
	CALL	ps2_send
	BTFSC	PS2_STATUS,PS2_RTS
	 GOTO	send_keydata_SAVECONTEXT
	GOTO	send_keydata_LOOP
;------------------------------------------------------------------------------
; バンクチェンジ
; 引数		CONFIG1,BANKSELECT
;------------------------------------------------------------------------------
bankchange
	MOVLW	1
	XORWF	CONFIG1,F
	BSF	DATANUMBER,NODATA	; ワンショットにするためデータを無効にする
	RETURN
;------------------------------------------------------------------------------
; 指定データを削除
;
; 引数
;	W		データ番号 0-127
;
; 使用変数
;	TMP12,13,20,21,22,23
;	FLASH_WORK	データ退避領域
;------------------------------------------------------------------------------
delete_data
	XORLW	1			; 削除する隣のデータ番号は?
	MOVWF	TMP12
	SWAPF	TMP12,W			; データの開始アドレス
	ANDLW	0xF0
	MOVWF	TMP20
	SWAPF	TMP12,W
	ANDLW	15
	ADDLW	HIGH KEYDATA
	MOVWF	TMP21
	BCF	STATUS,IRP		; ● IRP=0
	MOVLW	FLASH_WORK
	MOVWF	FSR
	CALL	read16_flash		; erase_flashで消えてしまう隣接データを退避させる
	CALL	erase_flash		; bit4-0は無視されるので、そのまま消去しても問題は出ない。
	MOVLW	FLASH_WORK
	MOVWF	FSR
	CALL	write16_flash		; 退避したデータを戻す
	RETURN
;------------------------------------------------------------------------------
; 登録一覧表を表示
;------------------------------------------------------------------------------
datalist
	MOVLW	LOW txt_listheader
	CALL	txstring

	CLRF	TMP00			; データ番号
datalist_LOOP
	CALL	prepare_tx_buffer
	MOVF	TMP00,W
	BSF	FLAGS,INDENT
	CALL	decimal8
	MOVLW	':'
	CALL	putchar
	
	SWAPF	TMP00,W			; データの開始アドレス
	ANDLW	0xF0
	MOVWF	TMP20
	SWAPF	TMP00,W
	ANDLW	15
	ADDLW	HIGH KEYDATA
	MOVWF	TMP21
	CALL	dump_data		; IRデータ表示

	BTFSC	STATUS,Z		; IRデータがあればキーデータも表示
	 GOTO	datalist_NEXT

	MOVLW	' '
	CALL	putchar
	MOVLW	'='
	CALL	putchar
	MOVLW	'>'
	CALL	putchar
	CALL	dump_data
	MOVLW	LOW txt_CRLF
	CALL	txstring_relative
datalist_NEXT
	INCF	TMP00,F
	MOVLW	128
	SUBWF	TMP00,W
	BTFSS	STATUS,Z
	 GOTO	datalist_LOOP
	RETURN

dump_data				; 16進数データの表示
	CALL	read_flash
	MOVF	TMP23,W			; データバイト数を読み取り、ループカウンタとする
	MOVWF	TMP01
	BTFSC	STATUS,Z		; サイズフィールドが0または16以上の時はデータ無しとする
	 GOTO	dump_data_NODATA
	SUBLW	15
	BTFSS	STATUS,C
	 GOTO	dump_data_NODATA
dump_data_LOOP
	MOVLW	' '
	CALL	putchar
	CALL	read_flash
	CALL	bin2hex
	INCF	TMP20,F
	DECFSZ	TMP01,F
	 GOTO	dump_data_LOOP
	BCF	STATUS,Z		; NZでリターンした時はデータがあった
	RETURN
dump_data_NODATA
	BSF	STATUS,Z		; Zでリターンした時はデータが無かった
	RETURN
;------------------------------------------------------------------------------
putchar
	MOVWF	INDF
	INCF	FSR,F
	RETURN
;------------------------------------------------------------------------------
; データ領域を初期化
;------------------------------------------------------------------------------
init_flash
	MOVLW	LOW KEYDATA
	MOVWF	TMP20
	MOVLW	HIGH KEYDATA
	MOVWF	TMP21
	MOVLW	64
	MOVWF	TMP22
init_LOOP
	CALL	erase_flash
	MOVLW	32
	ADDWF	TMP20,F
	BTFSC	STATUS,C
	 INCF	TMP21,F
	DECFSZ	TMP22,F
	 GOTO	init_LOOP
	RETURN
;------------------------------------------------------------------------------
; read_flash	プログラムメモリから1ワード読み込む
; erase_flash	プログラムメモリの32ワードブロックを消去する
; read16_flash	プログラムメモリから16ワードをFSRのアドレスに読み込む
; write16_flash	FSRのアドレスの16ワードをプログラムメモリへ書き込む
;
; 引数
;	TMP20		アドレスLSB(非破壊)
;	TMP21		アドレスMSB(非破壊)
;	FSR		32バイトバッファのアドレス(read16, write16)リトルエンディアンで書き込まれる
;	IRP		read16, write16用
;
; 戻り値
;	TMP22,W		データLSB(read)
;	TMP23		データMSB(read)
;
; 使用変数
;	TMP13		ループカウンタ(read16)
;	TMP22		ループカウンタ(write16)
;------------------------------------------------------------------------------
read_flash
	CALL	prepare_flash		; ◆ RP=3
	BSF	EECON1,RD
	NOP
	NOP
	BCF	STATUS,RP0		; ◆ RP=2
	MOVF	EEDATH,W
	MOVWF	TMP23
	MOVF	EEDATA,W
	MOVWF	TMP22
	BCF	STATUS,RP1		; ◆ RP=0
	RETURN
;------------------------------------------------------------------------------
read16_flash
	MOVLW	16
	MOVWF	TMP13
read16_flash_LOOP
	CALL	read_flash
	MOVF	TMP22,W
	CALL	putchar
	MOVF	TMP23,W
	CALL	putchar
	INCF	TMP20,F			; ブロックリードが目的なので繰り上がりは考慮しない
	DECFSZ	TMP13,F
	 GOTO	read16_flash_LOOP
	MOVLW	16			; 非破壊にするため16を引く
	SUBWF	TMP20,F
	RETURN
;------------------------------------------------------------------------------
erase_flash				; bit4-0は無視されて32ワードが一気に削除される。
	CALL	prepare_flash_CRSTOP	; ◆ RP=3
	BSF	EECON1,WREN
	BSF	EECON1,FREE
	BCF	INTCON,GIE
	CALL	write_sequence
	BCF	EECON1,FREE
	GOTO	WRITE16_END
;------------------------------------------------------------------------------
write16_flash
	CALL	prepare_flash_CRSTOP	; ◆ RP=3
	BSF	EECON1,WREN
	BCF	INTCON,GIE
	MOVLW	16
	MOVWF	TMP22
	BCF	STATUS,RP0		; ◆ RP=2
write16_flash_LOOP
	MOVF	INDF,W
	MOVWF	EEDATA
	INCF	FSR,F
	MOVF	INDF,W
	MOVWF	EEDATH
	INCF	FSR,F
	BSF	STATUS,RP0		; ◆ RP=3
	CALL	write_sequence
	BTFSC	EECON1,WR
	 GOTO	$-1
	BCF	STATUS,RP0		; ◆ RP=2
	INCF	EEADR,F			; ブロックライトが目的なので繰り上がりは考慮しない
	DECFSZ	TMP22,F
	 GOTO	write16_flash_LOOP
	BSF	STATUS,RP0		; ◆ RP=3
WRITE16_END
	BCF	EECON1,WREN
	BSF	INTCON,GIE
	BCF	STATUS,RP0		; ◆ RP=2
	BCF	STATUS,RP1		; ◆ RP=0
	BSF	RCSTA,CREN		; USART受信再開
	RETURN
;------------------------------------------------------------------------------
prepare_flash_CRSTOP
	BCF	RCSTA,CREN		; USART受信停止
prepare_flash				; アドレスとEEPGDのセットアップ
	BSF	STATUS,RP1		; ◆ RP=2
	MOVF	TMP20,W
	MOVWF	EEADR
	MOVF	TMP21,W
	MOVWF	EEADRH
	BSF	STATUS,RP0		; ◆ RP=3
	BSF	EECON1,EEPGD
	RETURN
;------------------------------------------------------------------------------
write_sequence
	MOVLW	0x55
	MOVWF	EECON2
	MOVLW	0xAA
	MOVWF	EECON2
	BSF	EECON1,WR
	NOP
	NOP
	RETURN
;------------------------------------------------------------------------------
; getstr		プログラムメモリからUSART送信バッファに文字列を読み込む
; getstr_relative	プログラムメモリからFSRの位置に文字列を読み込む
; txstr			getstr後に送信する
;
; 引数
;	TMP20		文字列アドレスLSB
;
; 戻り値
;	(FSR)		表示文字列(NULLで終端)
;	FSR		NULLの位置
;
; 使用変数
;	TMP20,21,22,23
;------------------------------------------------------------------------------
getstr_relative
	MOVWF	TMP20
	GOTO	getstr_1
getstr
	MOVWF	TMP20
	BSF	STATUS,IRP		; ● IRP=1
	MOVLW	LOW TX_BUFFER
	MOVWF	FSR
getstr_1
	MOVLW	HIGH txt_title		; 上位は自動的にセットする
	MOVWF	TMP21
	CALL	usart_tx_sync
getstr_LOOP
	CALL	read_flash
	RLF	TMP22,W
	RLF	TMP23,W
	ANDLW	0x7F
	MOVWF	INDF
	BTFSC	STATUS,Z
	 RETURN				; NULLならリターン

	INCF	FSR,F
	MOVF	TMP22,W
	ANDLW	0x7F
	MOVWF	INDF
	BTFSC	STATUS,Z
	 RETURN				; NULLならリターン

	INCF	FSR,F
	INCF	TMP20,F
	BTFSC	STATUS,Z
	 INCF	TMP21,F	
	GOTO	getstr_LOOP

txstring_relative
	CALL	getstr_relative
	GOTO	TXSTART_RETURN

txstring				; 引数 W=文字列のプログラムアドレス(LSB)
	CALL	getstr
TXSTART_RETURN
	CALL	usart_tx_start
	RETURN
;------------------------------------------------------------------------------
; strcmp		FSRの位置からプログラムメモリの比較文字列が含まれるか比較する
;
; 引数
;	TMP20		文字列アドレスLSB (MSBは自動でセットされる)
;	FSR		比較開始位置(非破壊)
;
; 戻り値
;	Z		1=一致、0=不一致
;
; 使用変数
;	TMP12,13,20,21,22,23
;------------------------------------------------------------------------------
strcmp
	MOVWF	TMP20
	MOVLW	HIGH txt_title
	MOVWF	TMP21

	MOVF	FSR,W
	MOVWF	TMP12
STRCMP_LOOP
	CALL	read_flash
	RLF	TMP22,W
	RLF	TMP23,W
	ANDLW	0x7F
	BTFSC	STATUS,Z
	 GOTO	STRCMP_END_Z		; NULLなら一致でリターン
	SUBWF	INDF,W
	BTFSS	STATUS,Z
	 GOTO	STRCMP_END_NZ		; ゼロにならないなら不一致でリターン

	INCF	FSR,F
	MOVF	TMP22,W
	ANDLW	0x7F
	BTFSC	STATUS,Z
	 GOTO	STRCMP_END_Z		; NULLなら一致でリターン
	SUBWF	INDF,W
	BTFSS	STATUS,Z
	 GOTO	STRCMP_END_NZ		; ゼロにならないなら不一致でリターン

	INCF	FSR,F
	INCF	TMP20,F
	BTFSC	STATUS,Z
	 INCF	TMP21,F	
	GOTO	STRCMP_LOOP

STRCMP_END_Z
	MOVF	TMP12,W
	MOVWF	FSR
	BSF	STATUS,Z
	RETURN

STRCMP_END_NZ
	MOVF	TMP12,W
	MOVWF	FSR
	BCF	STATUS,Z
	RETURN
;------------------------------------------------------------------------------
; 8bit 16進数文字列化
;
; 引数		W		変換元
;		FSR		文字列格納先のアドレス
; 戻り値	FSR		文字列の次のアドレス(自動的にNULLは代入しない)
; 使用変数	TMP13
;------------------------------------------------------------------------------
bin2hex
	MOVWF	TMP13
	SWAPF	TMP13,W
	CALL	bin2hex_2
	MOVF	TMP13,W
	CALL	bin2hex_2
	RETURN

bin2hex_2
	ANDLW	15
	ADDLW	'0'
	MOVWF	INDF
	SUBLW	'9'
	MOVLW	7
	BTFSS	STATUS,C
	 ADDWF	INDF,F
	INCF	FSR,F
	RETURN
;------------------------------------------------------------------------------
; 8bit 10進数文字列化してNULLターミネートする
;
; 引数		W		変換元の値
;		FSR		文字列格納先のアドレス
;		INDENT		スペースでインデントする、リターン時自動でOFFに戻る。
; 戻り値	FSR		文字列の次のNULLのアドレス
; 使用変数	TMP20,21,22,23
;------------------------------------------------------------------------------
decimal8
	MOVWF	TMP20			; 変換元
	MOVLW	3			; 最下位から代入するのでアドレスを進めておく
	ADDWF	FSR,W
	MOVWF	FSR
	MOVWF	TMP23			; 変換後の最終アドレス
	CLRF	INDF
	BCF	FLAGS,REPLACE_ZERO
	CALL	DEVIDE10_8		; 最下位
	ADDLW	0
	BTFSC	STATUS,Z
	 BSF	FLAGS,REPLACE_ZERO
	CALL	DEVIDE10_8
	BSF	FLAGS,REPLACE_ZERO
	CALL	DEVIDE10_8		; 最上位
	MOVF	TMP23,W
	MOVWF	FSR
	BCF	FLAGS,INDENT
	RETURN

DEVIDE10_8
	CLRF	TMP21			; 変換ワーク
	MOVLW	8			; 8bit分ループ
	MOVWF	TMP22			; ループカウンタ
DEVIDE10_8_LOOP
	BCF	STATUS,C
	RLF	TMP20,F
	RLF	TMP21,F
	MOVLW	256 - 10
	ADDWF	TMP21,W
	BTFSC	STATUS,C
	 MOVWF	TMP21
	BTFSC	STATUS,C
	 INCF	TMP20,F
	DECFSZ	TMP22,F
	 GOTO	DEVIDE10_8_LOOP
	DECF	FSR,F
	MOVF	TMP21,W
	MOVLW	DEL			; 見えない文字でパディング
	BTFSC	FLAGS,INDENT
	 MOVLW	' '			; スペースでパディング
	BTFSC	STATUS,Z
	BTFSS	FLAGS,REPLACE_ZERO
	 MOVLW	'0'	
	ADDWF	TMP21,W
	MOVWF	INDF
	RETURN
;------------------------------------------------------------------------------
; ASCII 10進数を8bitの数値に変換
;   FSRから対象文字まで自動で移動し変換開始、変換不能文字が来たところで変換終了。
;   見つからずNULLに当たればエラーを返す。
;
; 引数		IRP,FSR		対象文字列格納場所
; 戻り値	W		変換済み値
;		RESULT		0:正常終了 1:変換対象文字無し
;		FSR		変換できなくなったアドレス
; 使用変数	TMP22,23
;------------------------------------------------------------------------------
dec8bin
	BSF	FLAGS,RESULT
dec8bin_SEARCH
	MOVF	INDF,W
	BTFSC	STATUS,Z
	 RETURN
	CALL	isnumeric
	BTFSC	FLAGS,RESULT
	 GOTO	dec8bin_CONVERT
	INCF	FSR,F
	GOTO	dec8bin_SEARCH
dec8bin_CONVERT
	BCF	FLAGS,RESULT
	CLRF	TMP22
dec8bin_CONVERT_LOOP
	MOVLW	'0'
	SUBWF	INDF,W
	ADDWF	TMP22,F

	INCF	FSR,F
	MOVF	INDF,W
	CALL	isnumeric
	MOVF	TMP22,W
	BTFSS	FLAGS,RESULT
	 RETURN
	BCF	STATUS,C
	RLF	TMP22,F
	RLF	TMP22,W
	MOVWF	TMP23
	RLF	TMP23,W
	ANDLW	0xF8
	ADDWF	TMP22,F
	GOTO	dec8bin_CONVERT_LOOP
;------------------------------------------------------------------------------
; ASCII 16進数を8bitの数値に変換
;   FSRから対象文字まで自動で移動し変換開始、変換不能文字が来たところで変換終了。
;   A〜Fは小文字だけを受け付ける。
;   16進数はヒットした文字の末尾2文字が反映されるので、3文字以上で入力しない事。
;
; 引数		IRP,FSR		対象文字列格納場所
; 戻り値	W		変換済み値
;		RESULT		0:正常終了 1:変換対象文字無し
;		FSR		変換できなくなったアドレス
; 使用変数	TMP22,23
;------------------------------------------------------------------------------
hex8bin
	BSF	FLAGS,RESULT
hex8bin_SEARCH				; 16進数文字までポインタを進める
	MOVF	INDF,W
	BTFSC	STATUS,Z		; NULL終端チェック
	 RETURN
	CALL	ishex
	BTFSC	FLAGS,RESULT
	 GOTO	hex8bin_CONVERT
	INCF	FSR,F
	GOTO	hex8bin_SEARCH
hex8bin_CONVERT
	BCF	FLAGS,RESULT
	CLRF	TMP22			; 累積
hex8bin_CONVERT_LOOP
	MOVLW	'a'-10
	SUBWF	INDF,W
	BTFSS	STATUS,C
	 ADDLW	'a'-'9'-1
	ADDWF	TMP22,F

	INCF	FSR,F			; 次の文字が16進数なら継続
	MOVF	INDF,W
	CALL	ishex
	MOVF	TMP22,W
	BTFSS	FLAGS,RESULT
	 RETURN
	SWAPF	TMP22,W
	ANDLW	0xF0
	MOVWF	TMP22
	GOTO	hex8bin_CONVERT_LOOP
;------------------------------------------------------------------------------
; ASCII文字判定
;
; 引数		W	検査値
; 戻り値	RESULT	0:該当しない 1:該当する
; 使用変数	TMP23
;------------------------------------------------------------------------------
isnumeric
	MOVWF	TMP23
	BCF	FLAGS,RESULT
	MOVLW	'0'
	SUBWF	TMP23,W
	BTFSS	STATUS,C
	 RETURN
	MOVLW	'9'+1
	SUBWF	TMP23,W
	BTFSC	STATUS,C
	 RETURN
	BSF	FLAGS,RESULT
	RETURN
	
ishex
	MOVWF	TMP23
	CALL	isnumeric
	BTFSC	FLAGS,RESULT
	 RETURN

	MOVLW	'a'
	SUBWF	TMP23,W
	BTFSS	STATUS,C
	 RETURN
	MOVLW	'f'+1
	SUBWF	TMP23,W
	BTFSC	STATUS,C
	 RETURN
	BSF	FLAGS,RESULT
	RETURN
;------------------------------------------------------------------------------
; LED色変更
;
; 引数		W	LED出力ビット、色指定は色名で指定するほうが良い。
;------------------------------------------------------------------------------
set_color
	MOVWF	COLOR
	ANDLW	GREEN			; GREEN
	MOVWF	PORTB
	BCF	COLOR,LED_GREEN		; RED & BLUE
	MOVF	PORTA,W
	ANDLW	B'00011100'
	ADDWF	COLOR,W
	MOVWF	PORTA
	RETURN
;------------------------------------------------------------------------------
; 16bit 減算
;
; 引数
;	MATH00	LSB 引かれる数
;	MATH01	MSB
;	MATH10	LSB 引く数(非破壊)
;	MATH11	MSB
;
; 戻り値
;	MATH00	LSB 差
;	MATH01	MSB
;	C	キャリーフラグ
;	Z	ゼロフラグ
;------------------------------------------------------------------------------
sub16
	MOVF	MATH10,W		; 下位減算
	SUBWF	MATH00,F

	MOVF	MATH11,W		; 下位ボロー処理
	BTFSS	STATUS,C
	 INCF	MATH11,W
	SUBWF	MATH01,F		; 上位減算
	
	MOVF	MATH00,W		; 全ビットゼロ検出
	IORWF	MATH01,W
	RETURN
;------------------------------------------------------------------------------
wait10ms				; 10ms * W
	MOVWF	TMP21
WAIT1
	MOVLW	65
	MOVWF	TMP22
WAIT2
	CLRF	TMP23
WAIT3
	DECFSZ	TMP23,F
	 GOTO	WAIT3
	DECFSZ	TMP22,F
	 GOTO	WAIT2
	DECFSZ	TMP21,F
	 GOTO	WAIT1
	RETURN
;------------------------------------------------------------------------------
; 割り込みハンドラ
;------------------------------------------------------------------------------
; INT0割り込み
;
;  PS/2のクロックごとに割り込みが起動。
;  RTS状態をチェックし、受信要求フラグをセットする。
;  送信時は自己割り込みが発生しないようにINT0IEを0にセットする事。
;------------------------------------------------------------------------------
int0_handler
	BCF	INTCON,INT0IF
	BTFSS	PORTB,SDATA
	 BSF	PS2_STATUS,PS2_RTS
	GOTO	INT0_RETURN
;------------------------------------------------------------------------------
; Timer0割り込み
;
;  IRのリピートコード後、データを無効化するためのタイマ。
;  リピート判定後に13ms*DESTROY_TIMER経過してから無効化される。
;  データを無効化したくない場合は、TMR0IEを0にするとよい。
;------------------------------------------------------------------------------
timer0_handler
	BCF	INTCON,TMR0IF
	DECFSZ	DESTROY_COUNT,F
	 GOTO	TMR0_RETURN
	BCF	INTCON,TMR0IE		; 不要なので止める
	BSF	DATANUMBER,NODATA
	BCF	FLAGS,TEMPORARILY_DISABLE
	GOTO	TMR0_RETURN
;------------------------------------------------------------------------------
; TIMER1オーバーフロー割り込み
;
;  CCPでIR信号の立ち上がり検出から13ms経過すると、トレーラと判断し受信完了状態へ移行させる。
;------------------------------------------------------------------------------
timer1_handler
	BCF	PIR1,TMR1IF
	BSF	STATUS,RP0		; ◆ RP=1
	BCF	PIE1,TMR1IE		; 不要なので止める
	BCF	STATUS,RP0		; ◆ RP=0
	
	DECF	IR_STATUS,W		; RECIEVING(1)の時はRECIEVED(2)に移行
	BTFSC	STATUS,Z
	 INCF	IR_STATUS,F
	GOTO	TMR1_RETURN
;------------------------------------------------------------------------------
; CCP1トリガ割り込み
;
;  IR受信ルーチン
;  http://elm-chan.org/docs/ir_format.html
;------------------------------------------------------------------------------
ccp1_handler
	MOVLW	21			; CCP割り込み発生時にTMR1をクリアしたことにする
	MOVWF	TMR1L
	CLRF	TMR1H

	MOVF	PW0_L,W			; 以前のパルス幅を保存
	MOVWF	PW1_L
	MOVF	PW0_H,W
	MOVWF	PW1_H
	MOVF	CCPR1L,W
	MOVWF	PW0_L
	MOVF	CCPR1H,W
	MOVWF	PW0_H

	MOVLW	B'00000001'		; トリガエッジ変更
	XORWF	CCP1CON,F
	BTFSS	CCP1CON,CCP1M0
	 GOTO	CCP1_RISE
CCP1_FALL				; 立ち下りトリガで飛んできた時
	BSF	STATUS,RP0		; ◆ RP=1
	BCF	PIE1,TMR1IE		; トレーラ判定タイマ割り込み停止
	BCF	STATUS,RP0		; ◆ RP=0
	
	MOVLW	RECIEVED		; 前回の受信データが取り出されていない場合は中断する
	SUBWF	IR_STATUS,W
	BTFSC	STATUS,Z
	 GOTO	CCP1_END
	
	CLRF	NEW_FORMAT
AEHA_LEADER				; データ中にリーダーが来た場合、データよりも優先して検出するようになっている
	MOVF	PW1_L,W			; 前回のパルス幅でAEHAチェック
	MOVWF	MATH10
	MOVF	PW1_H,W
	MOVWF	MATH11

	MOVLW	LOW  (L1_AEHA * MIN_PERCENT / 100)
	MOVWF	MATH00
	MOVLW	HIGH (L1_AEHA * MIN_PERCENT / 100)
	MOVWF	MATH01
	CALL	sub16
	BTFSC	STATUS,C
	 GOTO	NEC_LEADER

	MOVLW	LOW  (L1_AEHA * MAX_PERCENT / 100)
	MOVWF	MATH00
	MOVLW	HIGH (L1_AEHA * MAX_PERCENT / 100)
	MOVWF	MATH01
	CALL	sub16
	BTFSS	STATUS,C
	 GOTO	NEC_LEADER

	RRF	MATH11,F		; データ0/1の閾値 = リーダー幅(8T) / 4
	RRF	MATH10,F
	RRF	MATH11,W
	ANDLW	B'00111111'
	MOVWF	THRESHOLD_H
	RRF	MATH10,W
	MOVWF	THRESHOLD_L
AEHA_LEADER2	
	MOVF	PW0_L,W			; 今回のパルス幅でリピートチェック
	MOVWF	MATH10
	MOVF	PW0_H,W
	MOVWF	MATH11

	MOVLW	LOW  (L2_AEHA * MIN_PERCENT / 100)
	MOVWF	MATH00
	MOVLW	HIGH (L2_AEHA * MIN_PERCENT / 100)
	MOVWF	MATH01
	CALL	sub16
	BTFSC	STATUS,C
	 GOTO	AEHA_REPEAT

	MOVLW	LOW  (L2_AEHA * MAX_PERCENT / 100)
	MOVWF	MATH00
	MOVLW	HIGH (L2_AEHA * MAX_PERCENT / 100)
	MOVWF	MATH01
	CALL	sub16
	BTFSS	STATUS,C
	 GOTO	AEHA_REPEAT

	MOVLW	AEHA
	MOVWF	NEW_FORMAT
	GOTO	LEADER_END

AEHA_REPEAT
	MOVLW	LOW  (R_AEHA * MIN_PERCENT / 100)
	MOVWF	MATH00
	MOVLW	HIGH (R_AEHA * MIN_PERCENT / 100)
	MOVWF	MATH01
	CALL	sub16
	BTFSC	STATUS,C
	 GOTO	NEC_LEADER

	MOVLW	LOW  (R_AEHA * MAX_PERCENT / 100)
	MOVWF	MATH00
	MOVLW	HIGH (R_AEHA * MAX_PERCENT / 100)
	MOVWF	MATH01
	CALL	sub16
	BTFSS	STATUS,C
	 GOTO	NEC_LEADER

	MOVLW	AEHA + REPEAT
	MOVWF	NEW_FORMAT
	GOTO	LEADER_END

NEC_LEADER	
	MOVF	PW1_L,W			; 前回のパルス幅でNECチェック
	MOVWF	MATH10
	MOVF	PW1_H,W
	MOVWF	MATH11

	MOVLW	LOW  (L1_NEC * MIN_PERCENT / 100)
	MOVWF	MATH00
	MOVLW	HIGH (L1_NEC * MIN_PERCENT / 100)
	MOVWF	MATH01
	CALL	sub16
	BTFSC	STATUS,C
	 GOTO	SONY_LEADER
	
	MOVLW	LOW  (L1_NEC * MAX_PERCENT / 100)
	MOVWF	MATH00
	MOVLW	HIGH (L1_NEC * MAX_PERCENT / 100)
	MOVWF	MATH01
	CALL	sub16
	BTFSS	STATUS,C
	 GOTO	SONY_LEADER
	
	RRF	MATH11,F		; データ0/1の閾値 = リーダー幅(16T) / 8
	RRF	MATH10,F
	RRF	MATH11,F
	RRF	MATH10,F
	RRF	MATH11,W
	ANDLW	B'00011111'
	MOVWF	THRESHOLD_H
	RRF	MATH10,W
	MOVWF	THRESHOLD_L
NEC_LEADER2
	MOVF	PW0_L,W			; 今回のパルス幅でリピートチェック
	MOVWF	MATH10
	MOVF	PW0_H,W
	MOVWF	MATH11

	MOVLW	LOW  (L2_NEC * MIN_PERCENT / 100)
	MOVWF	MATH00
	MOVLW	HIGH (L2_NEC * MIN_PERCENT / 100)
	MOVWF	MATH01
	CALL	sub16
	BTFSC	STATUS,C
	 GOTO	NEC_REPEAT
	MOVLW	LOW  (L2_NEC * MAX_PERCENT / 100)
	MOVWF	MATH00
	MOVLW	HIGH (L2_NEC * MAX_PERCENT / 100)
	MOVWF	MATH01
	CALL	sub16
	BTFSS	STATUS,C
	 GOTO	NEC_REPEAT

	MOVLW	NEC
	MOVWF	NEW_FORMAT
	GOTO	LEADER_END

NEC_REPEAT
	MOVLW	LOW  (R_NEC * MIN_PERCENT / 100)
	MOVWF	MATH00
	MOVLW	HIGH (R_NEC * MIN_PERCENT / 100)
	MOVWF	MATH01
	CALL	sub16
	BTFSC	STATUS,C
	 GOTO	SONY_LEADER

	MOVLW	LOW  (R_NEC * MAX_PERCENT / 100)
	MOVWF	MATH00
	MOVLW	HIGH (R_NEC * MAX_PERCENT / 100)
	MOVWF	MATH01
	CALL	sub16
	BTFSS	STATUS,C
	 GOTO	SONY_LEADER

	MOVLW	NEC + REPEAT
	MOVWF	NEW_FORMAT
	GOTO	LEADER_END

SONY_LEADER	
	MOVF	PW1_L,W			; 前回のパルス幅でSONYチェック
	MOVWF	MATH10
	MOVF	PW1_H,W
	MOVWF	MATH11

	MOVLW	LOW  (L1_SONY * MIN_PERCENT / 100)
	MOVWF	MATH00
	MOVLW	HIGH (L1_SONY * MIN_PERCENT / 100)
	MOVWF	MATH01
	CALL	sub16
	BTFSC	STATUS,C
	 GOTO	LEADER_END

	MOVLW	LOW  (L1_SONY * MAX_PERCENT / 100)
	MOVWF	MATH00
	MOVLW	HIGH (L1_SONY * MAX_PERCENT / 100)
	MOVWF	MATH01
	CALL	sub16
	BTFSS	STATUS,C
	 GOTO	LEADER_END

	RRF	MATH11,F		; データ0/1の閾値 = リーダー幅(4T) / 4 + リーダー幅(4T) / 8
	RRF	MATH10,F		;                 = 1.5T
	RRF	MATH11,W
	ANDLW	B'00111111'
	MOVWF	MATH11
	MOVWF	THRESHOLD_H
	RRF	MATH10,W
	MOVWF	THRESHOLD_L

	BCF	STATUS,C		; *0.5部分の加算
	RRF	MATH11,F
	RRF	MATH10,W
	ADDWF	THRESHOLD_L,F		; 下位加算
	MOVF	MATH11,W		; 最終キャリー処理
	BTFSC	STATUS,C
	 INCF	MATH11,W
	ADDWF	THRESHOLD_H,F		; 上位加算
SONY_LEADER2
	MOVF	PW0_L,W			; 今回のパルス幅チェック
	MOVWF	MATH10
	MOVF	PW0_H,W
	MOVWF	MATH11

	MOVLW	LOW  (L2_SONY * MIN_PERCENT / 100)
	MOVWF	MATH00
	MOVLW	HIGH (L2_SONY * MIN_PERCENT / 100)
	MOVWF	MATH01
	CALL	sub16
	BTFSC	STATUS,C
	 GOTO	LEADER_END
	MOVLW	LOW  (L2_SONY * MAX_PERCENT / 100)
	MOVWF	MATH00
	MOVLW	HIGH (L2_SONY * MAX_PERCENT / 100)
	MOVWF	MATH01
	CALL	sub16
	BTFSS	STATUS,C
	 GOTO	LEADER_END

	MOVLW	SONY
	MOVWF	NEW_FORMAT

LEADER_END
	MOVF	NEW_FORMAT,W		; リーダーが検出できなかった時はデータビットとする
	BTFSC	STATUS,Z
	 GOTO	DATABIT
	MOVWF	IR_FORMAT
	CLRF	IR_LENGTH
	MOVLW	1
	MOVWF	BITMASK
	MOVLW	RECIEVING
	MOVWF	IR_STATUS
	
	CLRF	TMR0			; リピート時にしばらくしてからデータを消す場合は以下の5行を有効にする
	BCF	INTCON,TMR0IF		; 13ms * DESTROY_TIMER 後に破棄される
	BSF	INTCON,TMR0IE		; |
	MOVLW	DESTROY_TIMER		; |
	MOVWF	DESTROY_COUNT		; v
	GOTO	CCP1_END

DATABIT
	MOVLW	RECIEVING
	SUBWF	IR_STATUS,W
	BTFSS	STATUS,Z
	 GOTO	INVALID_DATA

	MOVLW	SONY
	SUBWF	IR_FORMAT,W
	BTFSS	STATUS,Z
	 GOTO	DATA_AEHA

	MOVF	PW0_L,W			; SONYデータのパルス幅チェック
	MOVWF	MATH10
	MOVF	PW0_H,W
	MOVWF	MATH11

	MOVLW	LOW  D1MAX_SONY
	MOVWF	MATH00
	MOVLW	HIGH D1MAX_SONY
	MOVWF	MATH01
	CALL	sub16
	BTFSS	STATUS,C
	 GOTO	INVALID_DATA
	GOTO	CCP1_END

DATA_AEHA
	MOVLW	AEHA
	SUBWF	IR_FORMAT,W
	BTFSS	STATUS,Z
	 GOTO	DATA_NEC

	MOVF	PW1_L,W			; AEHAデータのパルス幅チェック
	MOVWF	MATH10
	MOVF	PW1_H,W
	MOVWF	MATH11
	MOVLW	LOW  D1MAX_AEHA
	MOVWF	MATH00
	MOVLW	HIGH D1MAX_AEHA
	MOVWF	MATH01
	CALL	sub16
	BTFSS	STATUS,C
	 GOTO	INVALID_DATA

	MOVF	PW0_L,W
	MOVWF	MATH10
	MOVF	PW0_H,W
	MOVWF	MATH11
	MOVLW	LOW  D2MAX_AEHA
	MOVWF	MATH00
	MOVLW	HIGH D2MAX_AEHA
	MOVWF	MATH01
	CALL	sub16
	BTFSS	STATUS,C
	 GOTO	INVALID_DATA
	GOTO	VALID_DATA

DATA_NEC
	MOVLW	NEC
	SUBWF	IR_FORMAT,W
	BTFSS	STATUS,Z
	 GOTO	INVALID_DATA

	MOVF	PW1_L,W			; NECデータのパルス幅チェック
	MOVWF	MATH10
	MOVF	PW1_H,W
	MOVWF	MATH11
	MOVLW	LOW  D1MAX_NEC
	MOVWF	MATH00
	MOVLW	HIGH D1MAX_NEC
	MOVWF	MATH01
	CALL	sub16
	BTFSS	STATUS,C
	 GOTO	INVALID_DATA

	MOVF	PW0_L,W
	MOVWF	MATH10
	MOVF	PW0_H,W
	MOVWF	MATH11
	MOVLW	LOW  D2MAX_NEC
	MOVWF	MATH00
	MOVLW	HIGH D2MAX_NEC
	MOVWF	MATH01
	CALL	sub16
	BTFSS	STATUS,C
	 GOTO	INVALID_DATA
	
VALID_DATA
	MOVLW	IRMAXLEN		; 最大受信長チェック
	SUBWF	IR_LENGTH,W
	BTFSC	STATUS,C
	 GOTO	CCP1_END

	RRF	IR_LENGTH,W		; 格納アドレス計算
	MOVWF	FSR
	RRF	FSR,F
	RRF	FSR,W
	ANDLW	B'00011111'
	ADDLW	LOW IR_BUFFER
	MOVWF	FSR

	COMF	BITMASK,W		; マスクを使って当該ビット以外のデータを取得
	ANDWF	INDF,W
	MOVWF	MASKDATA

	MOVF	PW0_L,W			; 閾値より長い発光時間はビットを1にする
	MOVWF	MATH10
	MOVF	PW0_H,W
	MOVWF	MATH11
	MOVF	THRESHOLD_L,W
	MOVWF	MATH00
	MOVF	THRESHOLD_H,W
	MOVWF	MATH01
	CALL	sub16
	MOVF	MASKDATA,W
	BTFSS	STATUS,C
	 IORWF	BITMASK,W
	MOVWF	INDF

	BCF	STATUS,C		; マスクを次の桁に更新
	RLF	BITMASK,F
	MOVLW	1
	BTFSC	STATUS,C
	 MOVWF	BITMASK
	INCF	IR_LENGTH,F		; 長さ更新
	GOTO	CCP1_END

INVALID_DATA
	CLRF	IR_FORMAT		; リーダーが検出できずRECIEVINGでもなかった場合、無効データとしてアイドルに戻る。
	CLRF	IR_STATUS
	GOTO	CCP1_END


CCP1_RISE				; 立ち上りトリガで飛んできた時
	BSF	STATUS,RP0		; ◆ RP=1
	BSF	PIE1,TMR1IE		; トレーラ判定タイマ割り込み許可
	BCF	STATUS,RP0		; ◆ RP=0

	MOVLW	SONY
	SUBWF	IR_FORMAT,W
	BTFSS	STATUS,Z
	 GOTO	CCP1_END
	
	MOVLW	RECIEVING
	SUBWF	IR_STATUS,W
	BTFSS	STATUS,Z
	 GOTO	CCP1_END

	MOVF	PW0_L,W			; SONYデータのパルス幅チェック
	MOVWF	MATH10
	MOVF	PW0_H,W
	MOVWF	MATH11
	MOVLW	LOW  D2MAX_SONY
	MOVWF	MATH00
	MOVLW	HIGH D2MAX_SONY
	MOVWF	MATH01
	CALL	sub16
	BTFSS	STATUS,C
	 GOTO	INVALID_DATA
	GOTO	VALID_DATA

CCP1_END
	BCF	PIR1,CCP1IF
	GOTO	CCP1_RETURN
;------------------------------------------------------------------------------
; USART割り込み送信
;
; 戻り値
;	USART_STATUS,TXING	バッファ内送信中フラグ
;------------------------------------------------------------------------------
usart_tx_handler
	BSF	STATUS,IRP		; ● IRP=1
	MOVF	USART_TXPTR,W
	MOVWF	FSR
	MOVF	INDF,W
	BTFSC	STATUS,Z		; NULLを見つけたら割り込みを無効化して送信を終了
	 GOTO	USART_TX_FINISH
	MOVWF	TXREG
	INCF	USART_TXPTR,F
	GOTO	UTX_RETURN
USART_TX_FINISH	
	BSF	STATUS,RP0		; ◆ RP=1
	BCF	PIE1,TXIE		; 送信割り込み停止
	BCF	STATUS,RP0		; ◆ RP=0
	BCF	USART_STATUS,TXING
	GOTO	UTX_RETURN

usart_tx_start
	CALL	usart_tx_sync
	BSF	USART_STATUS,TXING
	MOVLW	LOW TX_BUFFER
	MOVWF	USART_TXPTR
	BSF	STATUS,RP0		; ◆ RP=1
	BSF	PIE1,TXIE		; 割り込みを有効にすると、あとは勝手に送ってくれる。
	BCF	STATUS,RP0		; ◆ RP=0
	RETURN

usart_tx_sync
	BTFSC	USART_STATUS,TXING	; 送信中にバッファを書き換えないよう、動作をブロックさせる。
	 GOTO	usart_tx_sync
	RETURN	
;------------------------------------------------------------------------------
; USART割り込み受信 FIFO書き込み
;  バッファがいっぱいの場合は、データを書き込まない。
;  FIFOに入れるだけ。メモリサイズ節約のためfifo_writeを直接記述する。
;
; 引数
;	W		書き込むデータ(破壊)
; 戻り値
;	なし
; 使用変数
;	ITMP0		書き込むデータ
;------------------------------------------------------------------------------
usart_rx_handler
	MOVF	RCREG,W			; RCIFは自動でクリアされる
fifo_write
	MOVWF	ITMP0			; Wに入っている書き込みデータを退避
	MOVLW	FIFOSIZE		; バッファフルチェック
	SUBWF	USEDSIZE,W
	BTFSC	STATUS,Z		; オーバーフロー判定
	 GOTO	URX_RETURN

	MOVF	WRITEPTR,W		; バッファを間接アクセス
	MOVWF	FSR
	BSF	STATUS,IRP		; ● IRP=1
	MOVF	ITMP0,W
	MOVWF	INDF
	INCF	USEDSIZE,F

	INCF	WRITEPTR,F
	MOVLW	LOW RX_FIFOTOP		; ポインタのロールオーバーのチェック
	ADDLW	FIFOSIZE
	SUBWF	WRITEPTR,W
	MOVLW	LOW RX_FIFOTOP
	BTFSC	STATUS,Z
	 MOVWF	WRITEPTR		; ロールオーバーしていたならば、先頭位置に移動
	GOTO	URX_RETURN
;------------------------------------------------------------------------------
; FIFO読み出し
;	呼び出し前にバッファが空か調べること。
; 引数
;	なし
; 戻り値
;	W		読み出したデータ
; 使用変数
;	なし
;------------------------------------------------------------------------------
fifo_read
	MOVF	READPTR,W		; バッファを間接アクセス
	MOVWF	FSR
	BSF	STATUS,IRP		; ● IRP=1

	INCF	READPTR,F		; 読み出しポインタをインクリメント
	MOVLW	LOW RX_FIFOTOP		; ポインタのロールオーバーのチェック
	ADDLW	FIFOSIZE
	SUBWF	READPTR,W
	BTFSS	STATUS,Z
	 GOTO	FIFO_READ2		; そのままリターン
	MOVLW	LOW RX_FIFOTOP		; ロールオーバーしていたならば、先頭位置に移動
	MOVWF	READPTR
FIFO_READ2
	MOVF	INDF,W			; Wに読み込みデータを入れてリターン
	DECF	USEDSIZE,F		; FIFOのデータ数を-1(呼び出し時にチェックをしているので、マイナスになることはない)
	RETURN
;------------------------------------------------------------------------------
; 文字列リソース
;------------------------------------------------------------------------------
txt_title	DA	CRLF,"*** IRKey/PS2 Ver.0.46 ***",CRLF," set, rm, ls, bank, init, reset, ?"
txt_CRLF	DA	CRLF,NULL			; 位置を変えるな
txt_BS		DA	BS<<7|' ',BS<<7|NULL
txt_DELLINE	DA	ESC<<7|'[','G'<<7|ESC,'['<<7|'K',NULL
txt_listheader	DA	"Nr.: IR Code     => Keycod",'e'<<7|CR,LFNULL
txt_prompt	DA	'>'<<7|NULL			; 位置を変えるな

txt_del		DA	"rm",' '<<7|NULL
txt_list	DA	"ls",NULL
txt_init	DA	"init",NULL
txt_bank	DA	"bank",NULL
txt_reset	DA	"re"            		; 位置を変えるな
txt_set		DA	"se",'t'<<7|NULL		; 位置を変えるな
txt_help	DA	'?'<<7|NULL

txt_setmsg1	DA	CRLF,"Press button",'.'<<7|CR,LFNULL
txt_setmsg2	DA	CRLF,"Input keycode at most ",NULL
txt_setmsg3	DA	" bytes",'>'<<7|NULL

txt_long	DA	"Too long",'.'<<7|CR,LFNULL
txt_matched	DA	"] Matched.",CRLF,NULL
txt_stored	DA	"] Stored",'.'<<7|CR,LFNULL
txt_full	DA	"Memory full,  "        	; 位置を変えるな
txt_canceled	DA	"Canceled",'.'<<7|CR,LFNULL
txt_deleted	DA	"] Deleted.",CRLF,NULL
txt_inited	DA	"Initialized.",CRLF,NULL
txt_AEHA	DA	"AEHA",' '<<7|NULL
txt_SONY	DA	"SONY",' '<<7|NULL
txt_NEC		DA	"NEC ",NULL
txt_REPEAT	DA	"Repeat",' '<<7|NULL
txt_bits	DA	" bits:",' '<<7|NULL
;------------------------------------------------------------------------------
; リモコンデータ保存領域 16ワード×64×2バンク
; キーデータの先頭に00hが来た場合はバンクチェンジ、途中の場合はリピート無視。
;------------------------------------------------------------------------------
	ORG 0800H
KEYDATA		DA	408h,0F7h,01Bh,0E4h, 100h			; POWER		= BANK
	ORG 0810H
		DA	408h,0F7h,01Fh,0E0h, 295h,00h			; A		= Media Prev Track
	ORG 0820H
		DA	408h,0F7h,01Eh,0E1h, 22Bh,00h			; B		= f
	ORG 0830H
		DA	408h,0F7h,01Ah,0E5h, 2CDh,00h			; C		= Media Next Track
	ORG 0840H
		DA	408h,0F7h,004h,0FBh, 2B4h,00h			; CENTER	= Media Play/Pause
	ORG 0850H
		DA	408h,0F7h,005h,0FAh, 1B2h			; UP		= Media Volume +
	ORG 0860H
		DA	408h,0F7h,000h,0FFh, 1A1h			; DOWN		= Media Volume -
	ORG 0870H
		DA	408h,0F7h,008h,0F7h, 1EBh			; LEFT		= ←
	ORG 0880H
		DA	408h,0F7h,001h,0FEh, 1F4h			; RIGHT		= →
	ORG 0890H
		DA	0F23h,0CBh,26h,1,0,0,1Ch,0Dh,36h,5Bh,0,0,0,0,4
;------------------------------------------------------------------------------
; データEEPROM
;------------------------------------------------------------------------------
	ORG	2100H

	END