;------------------------------------------------------------------------------
; PS/2 to MSX(MU-1) マウスコンバータ
;
; 分解能JP設定       ホイールJP設定
;   JP1,JP2            JP3
;   Opn,Opn:1/2 400cpi Opn:ホイール検出を行う
;   Cls,Opn:1/4 800cpi Cls:ホイール検出を行わない
;   Opn,Cls:1/8 1600cpi
;   Cls,Cls:1/16 3200cpi
; 
;------------------------------------------------------------------------------
; バージョン履歴
; 2021/12/24	0.05c 分解能設定変更
; 2021/12/20	0.05b 最適化中 割り込み周りチェック終了
; 2021/12/19	0.05a 最適化中
; 2021/11/29	0.05 安定版
;		     ホイール分解能設定をジャンパで選択できるようにした
;		     分解能計算ルーチンを共通化
; 2021/11/28	0.04 スクロール反転廃止、スクロール初期化判定変更、センタークリック用途変更
;		     分解能の設定が反映されないマウスだったので、400dpi固定としてソフト側で処理するようにした。
;		     分解能変更アルゴリズムを整数処理から固定小数点に変更。ゆっくり移動させても確実に動くようになった。
; 2021/11/28	0.03 とりあえず動くようになった
; 2021/11/24	0.02 PS/2の通信ができるようになった
; 2021/11/22	0.01 S-760とMSXの通信ができるようになった
; 2021/11/21	0.00 基板完成
;------------------------------------------------------------------------------
; IRP,RP1,RP0を切り替える所で◆ RP=, ● IRP=をコメントに置く
; PCLATHを切り替える所で■ PCLATHをコメントに置く
; サブルーチンからの戻りの時、
;  RP1,RP0は0に戻してリターンする。
;  IRP,PCLATH,LCDモードは戻さずにリターンする。
; ルーチン呼び出しの時もRP=0を基本として記述している。
;------------------------------------------------------------------------------
; デバイス	PIC16F88
; 動作周波数	内蔵8MHz
;------------------------------------------------------------------------------
	LIST		P=PIC16F88,R=DEC,N=0
	INCLUDE		"P16F88.INC"
	ERRORLEVEL	0,-302,-306,-307
	__CONFIG _CONFIG1, 0x2F70	; FOSC : INTRC_RA6=IO
					; WDTE : Disable (ソフトウエアWDTを使用する)
					; PWRTE: Enable
					; MCLRE: RA5=MCLR
					; BODEN: Enable
					; LVP  : RB3=IO
					; CPD  : Not Protect
					; WRT  : Protection off
					; DEBUG: Disable
					; CCPMX: CCP1=RB3
					; CP   : Not Protect
	__CONFIG _CONFIG2, 0x3FFC	; FCMEN: Disable
					; IESO : Disable
	__IDLOCS 0x5C
;------------------------------------------------------------------------------
; 変数宣言
;------------------------------------------------------------------------------
; PS/2データリンク層
PS2_RXDATA	EQU	0x20		; 受信したデータ
PS2_TXDATA	EQU	0x21		; 送信するデータ
PS2_RSR		EQU	0x22		; 受信用シフトレジスタ
PS2_TSR		EQU	0x23		; 送信用シフトレジスタ
PS2_RCSTA	EQU	0x24		; 受信ステータス
PS2_TXSTA	EQU	0x41		; 送信ステータス
PS2_BITCOUNT	EQU	0x25		; 進行中ビット数カウンタ
PS2_PARITY	EQU	0x26		; パリティ計算用カウンタ
PS2_RESENDCOUNT	EQU	0x27		; リトライ回数カウンタ

LAST_OCTET1	EQU	0x29

COUNT_X		EQU	0x2A
COUNT_X_X	EQU	0x2B
COUNT_Y		EQU	0x2C
COUNT_Y_Y	EQU	0x2D
TMIT_X		EQU	0x2E
TMIT_Y		EQU	0x2F

RUN_COLOR	EQU	0x30
DIVISOR		EQU	0x31
FRACTION	EQU	0x32
INTEGER		EQU	0x33

PORTB_		EQU	0x6F
; 一時変数
TMP00		EQU	0x70
TMP01		EQU	0x71
;TMP02		EQU	0x72
;TMP03		EQU	0x73
TMP10		EQU	0x74
TMP11		EQU	0x75
W0		EQU	0x76
W1		EQU	0x77
INTWORK		EQU	0x78		; 割り込み用
COLORWORK	EQU	0x79

FLAG		EQU	0x7A
STB_PHASE	 EQU	0x7B		; STROBEのエッジカウンタ(ページ切り替え中に使うので共有領域に置く)

; 割り込みの退避用
W_		EQU	0x7C
STATUS_		EQU	0x7D
PCLATH_		EQU	0x7E
FSR_		EQU	0x7F
;------------------------------------------------------------------------------
; ポート割り当て
;------------------------------------------------------------------------------
; PORTA
;MSX_DATABIT	EQU	0-3		; OUT OD出力 ホスト側でプルアップ
PS2_CLOCK	EQU	4		; IN/OUT OD出力 抵抗でプルアップ PS/2クロック割り込みT0CKI
;MCLR		EQU	5		; /MCLR
MSX_LEFT	EQU	6		; OUT OD出力 ホスト側でプルアップ マウスボタン左 負論理
MSX_RIGHT	EQU	7		; OUT OD出力 ホスト側でプルアップ マウスボタン右 負論理

; PORTB
MSX_STROBE	EQU	0		; IN INT0
JP1		EQU	1		; IN JP入力 内部プルアップ
JP2		EQU	2		; IN JP入力 内部プルアップ
JP3		EQU	3		; IN JP入力 内部プルアップ
LED_R		EQU	4		; OUT LED
LED_B		EQU	5		; OUT LED
LED_G		EQU	6		; OUT LED
PS2_DATA	EQU	7		; IN/OUT OD出力 抵抗でプルアップ PS/2データ
;------------------------------------------------------------------------------
; 定数宣言
;------------------------------------------------------------------------------
; PS2_RCSTA
RECEIVED	EQU	0		; 受信済み
ERR_PARITY	EQU	1		; パリティエラー
ERR_OVERRUN	EQU	2		; オーバーランエラー
ERR_FRAME	EQU	3		; フレーミングエラー

; PS2_TXSTA
TXBUSY		EQU	0		; 0=受信 1=送信動作中
NAK		EQU	1		; 送信に対するNAK(1)/ACK(0)

RESENDMAX	EQU	2		; 何回目のRESEND要求送信でギブアップするか

PS2_LEFT	EQU	0
PS2_RIGHT	EQU	1
PS2_MIDDLE	EQU	2
ALIGNCHECK	EQU	3		; 1オクテット目のアライメントチェックビット
SIGN_X		EQU	4
SIGN_Y		EQU	5

; FLAG
CENTERSW	EQU	0		; センタークリックスイッチ
SIGN		EQU	1		; divideで使う符号ビット
ID_INTELLI	EQU	2		; マウスID
;--カラー定数------------------------------------------------------------------
BLACK		EQU	0
RED		EQU	1 << LED_R
GREEN		EQU	1 << LED_G
BLUE		EQU	1 << LED_B
YELLOW		EQU	RED + GREEN
MAGENTA		EQU	RED + BLUE
CYAN		EQU	GREEN + BLUE
WHITE		EQU	RED + GREEN + BLUE
LEDMASK		EQU	~WHITE

COL_POR		EQU	MAGENTA		; PS2MUのPOR
COL_WATCHDOG	EQU	WHITE		; ウォッチドッグ検出
COL_DETECT	EQU	YELLOW		; マウス初期化中
COL_STANDARD	EQU	GREEN		; 非スクロールモード MOUSEID=0
COL_INTELLI	EQU	BLUE		; スクロールモード MOUSEID=3
COL_CENTERSW	EQU	CYAN		; 中央ボタンON状態
COL_ERROR	EQU	RED		; エラー検出
;チェック済み------------------------------------------------------------------------------
; 電源ON
;------------------------------------------------------------------------------
;DEBUG	EQU	0
	ORG	0
	BSF	STATUS,RP0		; ◆ RP=1
	MOVLW	B'01110000'		; 8MHzに切り替え
	MOVWF	OSCCON
	GOTO	POWER_ON
;チェック済み------------------------------------------------------------------------------
; 割り込み分岐
;------------------------------------------------------------------------------
	ORG	4
INTERRUPT
	MOVWF	W_			; Wセーブ
	SWAPF	STATUS,W		; STATUSセーブ
	MOVWF	STATUS_
	MOVF	PCLATH,W		; PCLATHセーブ
	MOVWF	PCLATH_
	CLRF	STATUS			; ◆ RP=0
	CLRF	PCLATH			; ■ PCLATH
	
					; 全ての割り込みをチェックするためCALLで呼び出す
	BTFSC	INTCON,INT0IF		; STROBEエッジ検出 43us(86T)毎
	 CALL	int_STROBE		;  T=36,27,27,26
	BTFSC	INTCON,TMR0IF		; PS2クロック受信 30us(60T)毎
	 CALL	int_PS2CLOCK		;  RxT=s17,d21,p17,s28,f16 TxT=d25,p25,s25,a24,f20
	BTFSC	PIR1,TMR2IF		; STROBE監視タイムアウト 180us
	 CALL	int_STROBE_TIMEOUT	;  T=12
	BTFSC	PIR1,TMR1IF		; ホイールタイムアウト 32.768ms
	 CALL	int_WHEEL_TIMEOUT	;  T=10

	MOVF	PCLATH_,W		; PCLATHリストア
	MOVWF	PCLATH
	SWAPF	STATUS_,W		; STATUSリストア
	MOVWF	STATUS
	SWAPF	W_,F			; Wリストア
	SWAPF	W_,W
	RETFIE
;チェック済み------------------------------------------------------------------------------
int_STROBE_TIMEOUT			; STROBE監視タイムアウト、STROBE検出パラメータを初期値に戻して送信中のデータをオフにする
	BSF	STATUS,RP0		; ◆ RP=1
	BSF	OPTION_REG,INTEDG
	MOVLW	0xF0
	ANDWF	TRISA,F
	BCF	STATUS,RP0		; ◆ RP=0
	BCF	T2CON,TMR2ON
	BCF	PIR1,TMR2IF
	CLRF	STB_PHASE
	RETURN
;チェック済み------------------------------------------------------------------------------
int_WHEEL_TIMEOUT			; ホイールタイムアウト、ボタンをリリースしてタイマーを止める
	BSF	STATUS,RP0		; ◆ RP=1
	MOVLW	0xC0
	IORWF	TRISA,F
	BCF	STATUS,RP0		; ◆ RP=0
	CLRF	T1CON
	BCF	PIR1,TMR1IF
	RETURN
;チェック済み------------------------------------------------------------------------------
int_STROBE				; STROBEエッジ検出
	BCF	INTCON,INT0IF
	INCF	STB_PHASE,W
	MOVWF	STB_PHASE
	ADDWF	PCL,F
	 NOP
	 GOTO	STB0_X_HIGH		; 1
	 GOTO	STB1_X_LOW		; 2
	 GOTO	STB2_Y_HIGH		; 3
STB3_Y_LOW				; 4 Y下位送信
	 CLRF	STB_PHASE
	MOVF	TMIT_Y,W
	GOTO	TX_NIBBLE
;チェック済み---------------------------------------
STB2_Y_HIGH				; Y上位送信
	SWAPF	TMIT_Y,W
	GOTO	TX_NIBBLE
;チェック済み---------------------------------------
STB1_X_LOW				; X下位送信
	MOVF	TMIT_X,W
	GOTO	TX_NIBBLE
;チェック済み---------------------------------------
STB0_X_HIGH				; 送信データ生成とX上位送信
	BSF	T2CON,TMR2ON		; STROBE 180usタイマー起動
	COMF	COUNT_X,W		; MSXとPS/2ではX方向の符号が逆なので2の補数を使って直す
	ADDLW	1
	MOVWF	TMIT_X
	SUBLW	0x80			; -128を2の補数にすると-128に戻ってしまうので強制的に127に直す
	BTFSC	STATUS,Z
	 DECF	TMIT_X,F
	CLRF	COUNT_X

	MOVF	COUNT_Y,W		; Y方向はそのまま
	MOVWF	TMIT_Y
	CLRF	COUNT_Y

	SWAPF	TMIT_X,W
TX_NIBBLE				; 4ビット送信
	ANDLW	0x0F
	MOVWF	INTWORK
	BSF	STATUS,RP0		; ◆ RP=1
	MOVF	TRISA,W
	ANDLW	0xF0
	IORWF	INTWORK,W
	MOVWF	TRISA

	BSF	OPTION_REG,INTEDG	; 次回割り込みエッジセット
	BTFSC	STB_PHASE,0
	 BCF	OPTION_REG,INTEDG

	BCF	STATUS,RP0		; ◆ RP=0
	CLRF	TMR2			; タイムアウト検出カウンタリセット
	BCF	PIR1,TMR2IF		;  タイムアウトが同時発生していた時はSTROBE優先として無視する
	RETURN
;チェック済み------------------------------------------------------------------------------
int_PS2CLOCK				; PS2クロック受信
	MOVLW	0xFF
	MOVWF	TMR0
	BCF	INTCON,TMR0IF
	BTFSC	PS2_TXSTA,TXBUSY
	 GOTO	INT_PS2_SEND
;チェック済み---------------------------------------
INT_PS2_RECV				; PS/2受信動作
	MOVF	PS2_BITCOUNT,W
	ADDWF	PCL,F
	 GOTO	INT_PS2_RECV_START	; 0  スタートビット受信
	 GOTO	INT_PS2_RECV_DATA	; 1  bit0
	 GOTO	INT_PS2_RECV_DATA	; 2     1
	 GOTO	INT_PS2_RECV_DATA	; 3     2
	 GOTO	INT_PS2_RECV_DATA	; 4     3
	 GOTO	INT_PS2_RECV_DATA	; 5     4
	 GOTO	INT_PS2_RECV_DATA	; 6     5
	 GOTO	INT_PS2_RECV_DATA	; 7     6
	 GOTO	INT_PS2_RECV_DATA	; 8     7
	 GOTO	INT_PS2_RECV_PARITY	; 9  PARITY
	 GOTO	INT_PS2_RECV_STOP	; 10 STOP
INT_PS2_RECV_FIN			; 11 最終立ち上がり
	 BSF	STATUS,RP0		; ◆ RP=1
	BSF	OPTION_REG,T0SE
	BCF	STATUS,RP0		; ◆ RP=0
	CLRF	PS2_BITCOUNT
	RETURN
;チェック済み---------------------------------------
INT_PS2_RECV_STOP
	CLRF	PS2_RCSTA
	BTFSS	PORTB,PS2_DATA		; フレーミングチェック
	 BSF	PS2_RCSTA,ERR_FRAME

	BTFSS	PS2_PARITY,0		; ODDパリティチェック
	 BSF	PS2_RCSTA,ERR_PARITY

	BTFSC	PS2_RCSTA,RECEIVED	; バッファオーバーランチェック
	 BSF	PS2_RCSTA,ERR_OVERRUN

	MOVF	PS2_RSR,W		; データ保存
	MOVWF	PS2_RXDATA
	BSF	PS2_RCSTA,RECEIVED
INT_PS2_RECV_FINWAIT
	BSF	STATUS,RP0		; ◆ RP=1
	BCF	OPTION_REG,T0SE		; 最終クロックの立ち上がりを待つためエッジを切り替える
	BCF	STATUS,RP0		; ◆ RP=0
	INCF	PS2_BITCOUNT,F
	RETURN
;チェック済み---------------------------------------
INT_PS2_RECV_PARITY
	BTFSC	PORTB,PS2_DATA
	 INCF	PS2_PARITY,F
	INCF	PS2_BITCOUNT,F
	RETURN
;チェック済み---------------------------------------
INT_PS2_RECV_DATA
	BCF	STATUS,C
	BTFSC	PORTB,PS2_DATA
	 BSF	STATUS,C
	RRF	PS2_RSR,F
	BTFSC	PS2_RSR,7
	 INCF	PS2_PARITY,F
	INCF	PS2_BITCOUNT,F
	RETURN
;チェック済み---------------------------------------
INT_PS2_RECV_START
	BTFSS	PORTB,PS2_DATA
	 INCF	PS2_BITCOUNT,F		; スタートビット(=0)なら次のビットへ
	CLRF	PS2_PARITY
	RETURN
;チェック済み------------------------------------------------------------------------------
INT_PS2_SEND				; PS/2送信動作
	MOVF	PS2_BITCOUNT,W
	ADDWF	PCL,F
	 GOTO	INT_PS2_SEND_DATA	; 0  bit0準備
	 GOTO	INT_PS2_SEND_DATA	; 1     1
	 GOTO	INT_PS2_SEND_DATA	; 2	2
	 GOTO	INT_PS2_SEND_DATA	; 3     3
	 GOTO	INT_PS2_SEND_DATA	; 4     4
	 GOTO	INT_PS2_SEND_DATA	; 5     5
	 GOTO	INT_PS2_SEND_DATA	; 6     6
	 GOTO	INT_PS2_SEND_DATA	; 7     7
	 GOTO	INT_PS2_SEND_PARITY	; 8  PARITY
	 GOTO	INT_PS2_SEND_STOP	; 9  STOP
	 GOTO	INT_PS2_SEND_ACK	; 10 ACK受信立ち下がり
INT_PS2_SEND_FIN			; 11 最終立ち上がり
	 BCF	PS2_TXSTA,TXBUSY
	GOTO	INT_PS2_RECV_FIN
;チェック済み---------------------------------------
INT_PS2_SEND_DATA
	RRF	PS2_TSR,F
	BTFSC	STATUS,C
	 INCF	PS2_PARITY,F
INT_PS2_SEND_BIT
	BSF	STATUS,RP0		; ◆ RP=1
	BTFSS	STATUS,C
	 BCF	TRISB,PS2_DATA		; DATA=L
	BTFSC	STATUS,C
	 BSF	TRISB,PS2_DATA		; DATA=H
	BCF	STATUS,RP0		; ◆ RP=0
	INCF	PS2_BITCOUNT,F
	RETURN
;チェック済み---------------------------------------
INT_PS2_SEND_PARITY
	RRF	PS2_PARITY,W
	GOTO	INT_PS2_SEND_BIT
;チェック済み---------------------------------------
INT_PS2_SEND_STOP
	BSF	STATUS,C
	GOTO	INT_PS2_SEND_BIT
;チェック済み---------------------------------------
INT_PS2_SEND_ACK
	BCF	PS2_TXSTA,NAK
	BTFSC	PORTB,PS2_DATA
	 BSF	PS2_TXSTA,NAK
	GOTO	INT_PS2_RECV_FINWAIT
;------------------------------------------------------------------------------
; 電源投入時のSFR初期化
;------------------------------------------------------------------------------
POWER_ON				; ◆ RP=1
	BCF	STATUS,RP0		; ◆ RP=0
	CLRF	PORTA			; PORTAは変更しない事
	CLRF	PORTB_			; PORTBはPORTB_経由で変更すること
	CLRF	PORTB

	BSF	STATUS,RP0		; ◆ RP=1
	CLRF	ANSEL			; PORTAはデジタルI/Oで全てOD出力  OD出力のTRIS 0=L 1=HiZ
	MOVLW	B'11100000'		; [7]/RIGHT [6]/LEFT [5]/MCLR [4]PS2CLOCK [3-0]MSXBIT
	MOVWF	TRISA			;   起動直後はPS/2をINH(CLK=L)にしておく
	MOVLW	B'10001111'		; [7]PS2DATA(OD) [6-4]LEDOUT [3-1]JPIN [0]MSXSTROBE
	MOVWF	TRISB			;   PS2DATAとMSXSTROBEはRBPUで一括プルアップされるが問題なさそう

	MOVLW	B'01111110'		; [7]内部プルアップ有効、[6]STROBE立ち上げ割り込み、[5]TMR0外部クロック
	MOVWF	OPTION_REG		; [4]PS/2立ち下げ割り込み、[3]プリスケーラ=WDT [2-0]1:64 (ソフトウエアWDT=1秒)

	BSF	PIE1,TMR1IE		; スクロールのボタン解除32.768msを計測するタイマー割り込み
	BSF	PIE1,TMR2IE		; STROBE変化から180us経過を計測するタイマー割り込み
	MOVLW	180-11
	MOVWF	PR2

#IFNDEF DEBUG
	BTFSS	OSCCON,IOFS		; クロック切り替えが完了するまで待機
	 GOTO	$-1
#ENDIF

	BCF	STATUS,RP0		; ◆ RP=0
	MOVLW	B'00001000'		; ポストスケーラ1:2 1/2MHz*2*PR2=180us
	MOVWF	T2CON

	MOVLW	COL_POR
	BTFSS	STATUS,NOT_TO		; ウォッチドッグで再起動した時は起動色を変える
	 MOVLW	COL_WATCHDOG		; ウォッチドッグの検出ビットはMCLRでは解除されない事に注意(PORのみ)
	CALL	set_color
;------------------------------------------------------------------------------
; システムリセット、メインループ前処理
;------------------------------------------------------------------------------
SYSTEM_RESET
	CLRF	LAST_OCTET1
	CLRF	FLAG
	CLRF	COUNT_X
	CLRF	COUNT_X_X
	CLRF	COUNT_Y
	CLRF	COUNT_Y_Y
	CLRF	STB_PHASE
	CLRF	PS2_RCSTA
	CLRF	PS2_TXSTA
	CLRF	PS2_BITCOUNT
	MOVLW	0xFF			; あと1クロックでPS2割り込みがかかるようにしておく
	MOVWF	TMR0

	MOVLW	100			; MCLRで再起動したときの為に、充分なウェイトを入れてBATを読み飛ばす
	CALL	wait10ms		; マウスによってはINHが解除されてからBATが始まるものがある

	MOVLW	COL_DETECT		; マウス検出開始
	CALL	set_color

	CLRF	PIR1
	MOVLW	B'11110000'		; 割り込み開始 PEIE(TMR2,TMR1)+TMR0+INT0
	MOVWF	INTCON

INIT_MOUSE
	BSF	STATUS,RP0		; ◆ RP=1
	BSF	TRISA,PS2_CLOCK		; Clear INH
	BCF	STATUS,RP0		; ◆ RP=0

	MOVLW	0xFF			; RESET
	CALL	ps2_send_w
	CALL	acknowledge
	MOVLW	0xAA
	CALL	ps2_rxpolling
	MOVLW	0
	CALL	ps2_rxpolling

	BTFSS	PORTB,JP3		; ホイール初期化設定
	 GOTO	READ_ID			; JP3をジャンパするとホイールを使わない
	MOVLW	0xF3			; Set Intelli
	CALL	ps2_send_w
	CALL	acknowledge
	MOVLW	0xC8
	CALL	ps2_send_w
	CALL	acknowledge
	MOVLW	0xF3
	CALL	ps2_send_w
	CALL	acknowledge
	MOVLW	0x64
	CALL	ps2_send_w
	CALL	acknowledge
	MOVLW	0xF3
	CALL	ps2_send_w
	CALL	acknowledge
	MOVLW	0x50
	CALL	ps2_send_w
	CALL	acknowledge

	MOVLW	0xF3			; Set Sample Rate 40
	CALL	ps2_send_w
	CALL	acknowledge
	MOVLW	0x28
	CALL	ps2_send_w
	CALL	acknowledge
	
READ_ID
	MOVLW	0xF2			; Read ID
	CALL	ps2_send_w
	CALL	acknowledge
	CALL	ps2_recvpolling
	BTFSC	STATUS,Z
	 GOTO	DETECT_OK
	SUBLW	3
	BTFSS	STATUS,Z
	 GOTO	DETECT_ERROR
	BSF	FLAG,ID_INTELLI
	GOTO	DETECT_OK
DETECT_ERROR
	MOVLW	COL_ERROR
	CALL	set_color
	GOTO	$

DETECT_OK
	MOVLW	COL_STANDARD
	BTFSC	FLAG,ID_INTELLI
	 MOVLW	COL_INTELLI
	MOVWF	RUN_COLOR
	CALL	set_color

	MOVLW	0xF4			; Enable
	CALL	ps2_send_w
	CALL	acknowledge
;------------------------------------------------------------------------------
; メインループ
;------------------------------------------------------------------------------
MAINLOOP
	CALL	ps2_recvpolling
	MOVWF	TMP00			; 以降RP=1でボタンを判定するためTMPに入れておく
	BTFSS	TMP00,ALIGNCHECK	; 必須ビットが0の場合ストリーム同期できていないので戻る
	 GOTO	MAINLOOP		;  そもそもこの状態に陥るとカーソルが正常に動かないはず

	BSF	STATUS,RP0		; ◆ RP=1
	BCF	INTCON,GIE		; ボタン更新中にTMR1割り込みでボタンがクリアされないように禁止する
	MOVF	TRISA,W			; マウスボタン判定
	ANDLW	B'00111111'
	BTFSS	TMP00,PS2_LEFT
	 ADDLW	1 << MSX_LEFT
	BTFSS	TMP00,PS2_RIGHT
	 ADDLW	1 << MSX_RIGHT
	MOVWF	TRISA
	BSF	INTCON,GIE
	BCF	STATUS,RP0		; ◆ RP=0

	BTFSC	TMP00,PS2_MIDDLE	; 今回センタークリックされたらスイッチフラグを反転させる
	 BTFSC	LAST_OCTET1,PS2_MIDDLE
	  GOTO	SAVE_OCTET1
	MOVLW	1
	XORWF	FLAG,F
	MOVF	RUN_COLOR,W
	BTFSC	FLAG,CENTERSW
	 MOVLW	COL_CENTERSW
	CALL	set_color
SAVE_OCTET1
	MOVF	TMP00,W
	MOVWF	LAST_OCTET1
;---------------------------------------
SECOND_OCTET
	CALL	ps2_recvpolling
	MOVWF	INTEGER
	BCF	FLAG,SIGN
	BTFSC	LAST_OCTET1,SIGN_X
	 BSF	FLAG,SIGN
	
	RRF	PORTB,W			; JP1,2から分周設定を取り込む
	XORLW	3
	ANDLW	3
	BTFSC	FLAG,CENTERSW
	 ADDLW	1
	MOVWF	DIVISOR

	CALL	divide
	MOVF	FRACTION,W
	ADDWF	COUNT_X_X,F
	BTFSC	STATUS,C
	 INCF	INTEGER,F
	MOVF	INTEGER,W
	ADDWF	COUNT_X,F
;---------------------------------------
THIRD_OCTET
	CALL	ps2_recvpolling
	MOVWF	INTEGER
	BCF	FLAG,SIGN
	BTFSC	LAST_OCTET1,SIGN_Y
	 BSF	FLAG,SIGN

	CALL	divide
	MOVF	FRACTION,W
	ADDWF	COUNT_Y_Y,F
	BTFSC	STATUS,C
	 INCF	INTEGER,F
	MOVF	INTEGER,W
	ADDWF	COUNT_Y,F

	BTFSS	FLAG,ID_INTELLI		; ID=0なら3オクテットで終了
	 GOTO	MAINLOOP
;---------------------------------------
FOURTH_OCTET
	CALL	ps2_recvpolling
	ANDLW	0x0F
	BTFSC	STATUS,Z		; スクロール量が0なら処理不要
	 GOTO	MAINLOOP

	MOVWF	TMP00			; 以降RP=1でホイール方向を判定するためTMPに入れておく
	CLRF	TMR1L			; 32msオートリリースタイマー起動
	CLRF	TMR1H			; 速くスクロールすると1オクテット目のボタンアップと合わさって
	BSF	T1CON,TMR1ON		;  25ms(40Hz)でリリースされるトリガーになる
	BSF	STATUS,RP0		; ◆ RP=1
	BCF	INTCON,GIE
	MOVF	TRISA,W
	ANDLW	B'00111111'
	BTFSS	TMP00,3			; 方向が+なら右クリック、-なら左クリック
	 ADDLW	1 << MSX_RIGHT
	BTFSC	TMP00,3
	 ADDLW 1 << MSX_LEFT
	MOVWF	TRISA
	BSF	INTCON,GIE
	BCF	STATUS,RP0		; ◆ RP=0
	GOTO	MAINLOOP
;------------------------------------------------------------------------------
; 指定の値を受信するまで戻らない
;
; 引数		W	照合データ
; 戻り値	なし
; 使用変数	TMP00
;------------------------------------------------------------------------------
acknowledge
	MOVLW	0xFA
ps2_rxpolling
	MOVWF	TMP00
RXPOLLING_LOOP
	CALL	ps2_recvpolling
	SUBWF	TMP00,W
	BTFSC	STATUS,Z
	 RETURN
	GOTO	RXPOLLING_LOOP
;------------------------------------------------------------------------------
; タイムアウト無しの受信待ち
;  PERRとFERRは再送要求を出す
;
; 引数		なし
; 戻り値	W,PS2_RXDATA	受信データ
; 使用変数	なし
;------------------------------------------------------------------------------
ps2_recvpolling
	MOVLW	RESENDMAX
	MOVWF	PS2_RESENDCOUNT
PS2_RECVPOLLING_LOOP
	BTFSS	PS2_RCSTA,RECEIVED
	 GOTO	PS2_RECVPOLLING_LOOP

	BCF	PS2_RCSTA,RECEIVED
	BTFSS	PS2_RCSTA,ERR_PARITY		; PERRかFERRは再送要求
	 BTFSC	PS2_RCSTA,ERR_FRAME
	  GOTO PS2_RECV_PHYERR
	MOVF	PS2_RXDATA,W
	RETURN				; 問題なければリターン

PS2_RECV_PHYERR
	DECFSZ	PS2_RESENDCOUNT,F	; 再送でトライ
	 GOTO	PS2_RECV_PHYERR_REQRESEND
	;GOTO	$			; 再送オーバーで停止させる場合
	;RETURN				; 再送オーバーでエラーのまま戻る場合
					; 両方コメントアウトすると無限に再送リトライする
PS2_RECV_PHYERR_REQRESEND
	MOVLW	0xFE
	MOVWF	PS2_TXDATA
	CALL	ps2_send
	GOTO	PS2_RECVPOLLING_LOOP
;------------------------------------------------------------------------------
; PS/2送信開始処理
; 送信動作中(TXBUSY=1)は現在の送信が終了して次の送信が開始できるまで戻らない。
; 受信動作中(TXBUSY=0)かつ、データビット7のクロックを受信済み(PS2_BITCOUNT=9)の場合、
;  現在の受信が完了し(PS2_BITCOUNT<9に変化する)、次の送信が開始できるまで戻らない。
;  その場合、送信完了後に送られてくる応答パケットまでに受信データを取り出さないとオーバーランエラーになるので注意する事。
;  規格では10だが比較タイムラグでチェック漏れを防ぐため9にしている。
;
; 引数		W、またはPS2_TXDATA	送信データ
; 戻り値	なし
; 使用変数	なし
;------------------------------------------------------------------------------
ps2_send_w
	MOVWF	PS2_TXDATA
ps2_send
	MOVLW	10
	SUBWF	PS2_BITCOUNT,W
	BTFSS	PS2_TXSTA,TXBUSY	; 既に送信動作中は終わるまで待つ
	 BTFSC	STATUS,C
	  GOTO	ps2_send

	BSF	PS2_TXSTA,TXBUSY
	MOVF	PS2_TXDATA,W
	MOVWF	PS2_TSR
	MOVLW	1
	MOVWF	PS2_PARITY
	CLRF	PS2_BITCOUNT

	DECF	TMR0,F			; INHにした時に自身で割り込みを発生させてしまわないように1カウント無視する
	BSF	STATUS,RP0		; ◆ RP=1
	BCF	TRISA,PS2_CLOCK		; INH
	CALL	wait100us

	BCF	TRISB,PS2_DATA		; RTS
	CALL	wait100us

	BSF	TRISA,PS2_CLOCK		; 擬似スタートビット
	BCF	STATUS,RP0		; ◆ RP=0
	RETURN
;チェック済み------------------------------------------------------------------------------
; 2で割る・PS/2は1+8bit、MSXは1+7bitと1bit余るので少なくとも一度は割る
;
; 引数		SIGN,INTEGER,DIVISOR
; 戻り値	INTEGER,FRACTION
; 使用変数	なし
;------------------------------------------------------------------------------
divide
	CLRF	FRACTION
	INCF	DIVISOR,W
DIVIDE_LOOP
	BCF	STATUS,C
	BTFSC	FLAG,SIGN
	 BSF	STATUS,C
	RRF	INTEGER,F		; 小数点以上の7ケタ
	RRF	FRACTION,F
	ADDLW	0xFF
	BTFSS	STATUS,Z
	 GOTO	DIVIDE_LOOP
	RETURN
;チェック済み------------------------------------------------------------------------------
; LED色変更
;
; 引数		W	LED出力色、色指定は色名で指定するほうが良い。
; 使用変数	COLORWORK
;------------------------------------------------------------------------------
set_color
	MOVWF	COLORWORK
	MOVF	PORTB_,W
	ANDLW	LEDMASK
	IORWF	COLORWORK,W
	MOVWF	PORTB_
	MOVWF	PORTB
	RETURN
;チェック済み------------------------------------------------------------------------------
; 約10ms * Wウェイト
;
; 引数		W
; 使用変数	TMP12,13
;------------------------------------------------------------------------------
wait10ms
	MOVWF	W0
WAIT0
	MOVLW	74
	MOVWF	W1
WAIT1
	MOVLW	66
	CALL	wait_2W_2us
	DECFSZ	W1,F
	 GOTO	WAIT1
	DECFSZ	W0,F
	 GOTO	WAIT0
	RETURN
;チェック済み------------------------------------------------------------------------------
; W*2us+2usウェイト
;
; 引数		なし
; 使用変数	なし
;------------------------------------------------------------------------------
wait100us
	MOVLW	49
wait_2W_2us
	ADDLW	0xFF
	BTFSS	STATUS,Z
	 GOTO	wait_2W_2us
	RETURN
;------------------------------------------------------------------------------
	END