;---- simple data transfer chip RX
;---- 簡易データ受信チップ・ファームウェア
;---- by K.I	since 020322

	LIST	P=12C509A
	INCLUDE	P12C509A.INC

	__CONFIG _MCLRE_OFF & _CP_OFF & _WDT_OFF & _IntRC_OSC
	__IDLOCS H'0100'	;V1.00

;--------------------------------------------------------
;--------------------------------------------------------
NOOP	MACRO			;debug用
	GOTO	$+2		;skip dummy
	XORLW	H'FF'		;dummy
	ENDM

REPAIR	MACRO	jump_to		;NOOP置換え用
	NOP
	GOTO	jump_to
	ENDM
;--------------------------------
RXE		EQU	0		;GP0, O, 受信イネーブル
OUT0		EQU	1		;GP1, O, bit0
OUT1		EQU	2		;GP2, O, bit1
RXD		EQU	3		;GP3, I, 受信データ
OUT2		EQU	4		;GP4, O, bit2
OUT3		EQU	5		;GP5, O, bit3
;--------------------------------

GPIODIR		EQU	B'00001000'	;GPIO入出力設定
GPIOINI		EQU	B'00000000'	;GPIO InitValue
OPT_INI		EQU	B'00011100'	;OPTION Init

TX_IDLE		EQU	B'00011111'	; 'idle'
TX_START	EQU	B'00011000'	; 'start'
TX_END		EQU	B'00001101'	; 'end'
TX_ERR		EQU	B'00000100'	; 'error'

RX_MIN		EQU	40
RX_N_MIN	EQU	80		;通信速度の範囲(7サイクル単位)
RX_N_MAX	EQU	160		;(計算値は119で約833US)
;--------------------------------

	CBLOCK	H'07'		;12C509は07~1Fが汎用レジスタ

	RXDATA			;受信データ
	GPIOX			;前回のGPIO値
	OUTDATA			;データ出力用バッファ
	OUTBUF

	RXCOUNT			;RX汎用カウンタ
	RX_N0			;Rx count0
	RX_N1			;Rx count1
	RX_N2			;Rx count2
	RX_N3			;Rx count3
	RX_N4			;Rx count4
	RX_N

	WAIT_CN			;WAIT counter
	WAIT_CN2		;WAIT counter2

	LINE			;汎用バッファ
	BL_CN
	ERR_NO			;Error Number

	ENDC
;--------------------------------------------------------
;--------------------------------------------------------
	ORG	0		;リセット・ベクタ
;--------------------------------------------------------

POWERUP
	GOTO	SETUPPORTS

;--------------------------------------------------------
	ORG	4
;--------------------------------------------------------
	DT	"RX021116E"	;<-- **** Version ****
;--------------------------------------------------------
MAIN
	NOOP
	MOVF	GPIO,W
	MOVWF	GPIOX		;現在のGPIOを保存

				;---- idle信号を利用して通信速度を求める
	CALL	RXINVCHK
	MOVLW	255-RX_MIN	;RX_MIN幅より小さければ雑音として無視する
	ADDWF	RXCOUNT,W
	BTFSS	STATUS,C
	SLEEP
	MOVF	RXCOUNT,W
	MOVWF	RX_N0		;RX_N0←B0-B1の時間

	CALL	RXINVCHK
	MOVLW	255-RX_MIN	;RX_MIN幅より小さければ雑音として無視する
	ADDWF	RXCOUNT,W
	BTFSS	STATUS,C
	SLEEP
	MOVF	RXCOUNT,W
	MOVWF	RX_N1		;RX_N1←B1-B2の時間

	CALL	RXINVCHK
	MOVLW	255-RX_MIN	;RX_MIN幅より小さければ雑音として無視する
	ADDWF	RXCOUNT,W
	BTFSS	STATUS,C
	SLEEP
	MOVF	RXCOUNT,W
	MOVWF	RX_N2		;RX_N2←B2-B3の時間

	CALL	RXINVCHK
	MOVLW	255-RX_MIN	;RX_MIN幅より小さければ雑音として無視する
	ADDWF	RXCOUNT,W
	BTFSS	STATUS,C
	SLEEP
	MOVF	RXCOUNT,W
	MOVWF	RX_N3		;RX_N3←B3-B4の時間

	CALL	RXINVCHK
	MOVLW	255-RX_MIN	;RX_MIN幅より小さければ雑音として無視する
	ADDWF	RXCOUNT,W
	BTFSS	STATUS,C
	SLEEP
	MOVF	RXCOUNT,W
	MOVWF	RX_N4		;RX_N4←B4-B5の時間

	CALL	RX_CALC		;(RX_N3+RX_N4)/4→RX_Nを求める

	CALL	WAIT_HALF	;RX_N/2待ち(信号の中心位置を見るため)

	CALL	READ_1B		;1ビット読み込み
	BTFSS	STATUS,C
	GOTO	CHK_START	;信号0ならばSTARTチェックへ

	CALL	READ_1B		;1ビット読み込み
	BTFSS	STATUS,C
	GOTO	CHK_START	;信号0ならばSTARTチェックへ

	GOTO	ERR_SLEEP

CHK_START
	CALL	READ_1B		;1ビット読み込み
	BTFSC	STATUS,C	;2個目の0信号をチェック
	GOTO	ERR_SLEEP

	CALL	READ_1B		;1ビット読み込み
	BTFSC	STATUS,C	;3個目の0信号をチェック
	GOTO	ERR_SLEEP

START				;3個0信号が続いたのでスタート
	NOOP			;(START=11000)

DATA_LOOP
	NOOP
	CALL	READ_5B		;READ_5B→データを5ビット分読む
	MOVF	RXDATA,W
	XORLW	TX_END
	BTFSC	STATUS,Z
	GOTO	DATA_END	;ENDなら終わり

	CALL	OUT_5B		;データの表示出力
	GOTO	DATA_LOOP	;繰り返し

DATA_END
	CALL	WAIT_250MS	;約0.75秒待ってから
	CALL	WAIT_250MS	;(Tx側は連続モードに入るのに約1秒
	CALL	WAIT_250MS	; なのでその前にSLEEPに入るように)
	NOP

FADE_OUT
	CALL	BLINK_OFF	;表示をOFFして
	SLEEP			;SLEEPに入る

;--------------------------------------------------------
; 5bit-NRZI 信号の受信

READ_5B
	CALL	READ_1B		;Read bit0
	MOVWF	RXDATA
	CALL	READ_1B		;Read bit1
	RLF	RXDATA,F
	CALL	READ_1B		;Read bit2
	RLF	RXDATA,F
	CALL	READ_1B		;Read bit3
	RLF	RXDATA,F
	CALL	READ_1B		;Read bit4
	RLF	RXDATA,F

	RETLW	0

;--------------------------------------------------------
; 5bitデータを5B/4Bテーブルで、4bitに変換して出力する

OUT_5B
	NOOP
	CALL	CNV_RXDATA
	NOOP
	MOVWF	OUTDATA

	NOOP
	BTFSC	OUTDATA,7	;bit7 check
	GOTO	ERR_DATA
OUT_4B
	NOOP
	MOVF	GPIO,W
	ANDLW	B'00000001'	;RXE COPY
	MOVWF	OUTBUF

	BTFSC	OUTDATA,0	;bit0 check
	BSF	OUTBUF,OUT0	;OUT0 set
	BTFSC	OUTDATA,1	;bit1 check
	BSF	OUTBUF,OUT1	;OUT1 set
	BTFSC	OUTDATA,2	;bit2 check
	BSF	OUTBUF,OUT2	;OUT2 set
	BTFSC	OUTDATA,3	;bit3 check
	BSF	OUTBUF,OUT3	;OUT3 set
	MOVF	OUTBUF,W

	MOVWF	GPIO

	RETLW	0

;--------------------------------------------------------
; RXが反転するまでカウントしてRXCOUNTを返す
; 前回のGPIO値をGPIOXにセットしてコール

RXINVCHK
	CLRF	RXCOUNT

	BTFSS	GPIOX,RXD	;前回のGPIO値
	GOTO	WRISE		;Low
	GOTO	WFALL		;High

;--------------------------------------------------------
; READ 1bit
;		RX_Nの間にデータが反転しなければ、信号0
;		反転すれば同期をとって(カウンタリセット)
;		RX_N/2の間反転しなければ、信号1とする。
;		反転したら、エラー

READ_1B
	MOVF	RX_N,W
	MOVWF	RXCOUNT		;RX_NをRXCOUNTにセット

	BTFSS	GPIOX,RXD	;前回のGPIO値
	GOTO	R1B_WRISE	;Low
	GOTO	R1B_WFALL	;High

;--------------------------------------------------------
; RX_N/2待ちルーチン。途中で反転したらエラー

WAIT_HALF
	NOOP
	BCF	STATUS,C
	RRF	RX_N,W
	MOVWF	RXCOUNT		;RX_N/2をRXCOUNTにセット

	BTFSS	GPIOX,RXD	;前回のGPIO値
	GOTO	WH_WRISE	;Low
	GOTO	WH_WFALL	;High

;--------------------------------------------------------
; RX速度計算と範囲チェック

RX_CALC
	BCF	STATUS,C
	MOVF	RX_N3,W		;RXの速度計算
	ADDWF	RX_N4,W
	MOVWF	RX_N
	RRF	RX_N,F		;RX_N=(RX_N3+RX_N4)/2

	MOVLW	RX_N_MAX-RX_N_MIN+1
	MOVWF	LINE
	MOVLW	255-RX_N_MAX	;RX_N範囲はRX_N_MIN~RX_N_MAX
	ADDWF	RX_N,W
	ADDWF	LINE,W

	BTFSS	STATUS,C	;RX_Nが範囲外であれば、ERR_RANGE
	GOTO	ERR_RANGE

	RETLW	0

;--------------------------------------------------------
;RXDATAの値を5B/4B変換してWに入れて返す

CNV_RXDATA
	NOOP
	MOVF	RXDATA,W	;WにRXDATA(5bit)を入れる

TAB5B4B				;4B/5B変換用テーブル
	ADDWF	PCL,F
	RETLW	B'11111111'	; 00
	RETLW	B'11111111'	; 01
	RETLW	B'11111111'	; 02
	RETLW	B'11111111'	; 03
	RETLW	B'11111111'	; 04
	RETLW	B'11111111'	; 05
	RETLW	B'11111111'	; 06
	RETLW	B'11111111'	; 07
	RETLW	B'11111111'	; 08
	RETLW	B'00000001'	; 09-->'1'
	RETLW	B'00000100'	; 0A-->'4'
	RETLW	B'00000101'	; 0B-->'5'
	RETLW	B'11111111'	; 0C
	RETLW	B'11111111'	; 0D-->'end'
	RETLW	B'00000110'	; 0E-->'6'
	RETLW	B'00000111'	; 0F-->'7'
	RETLW	B'11111111'	; 10
	RETLW	B'11111111'	; 11
	RETLW	B'00001000'	; 12-->'8'
	RETLW	B'00001001'	; 13-->'9'
	RETLW	B'00000010'	; 14-->'2'
	RETLW	B'00000011'	; 15-->'3'
	RETLW	B'00001010'	; 16-->'A'
	RETLW	B'00001011'	; 17-->'B'
	RETLW	B'11111111'	; 18-->'start'
	RETLW	B'11111111'	; 19
	RETLW	B'00001100'	; 1A-->'C'
	RETLW	B'00001101'	; 1B-->'D'
	RETLW	B'00001110'	; 1C-->'E'
	RETLW	B'00001111'	; 1D-->'F'
	RETLW	B'00000000'	; 1E-->'0'
	RETLW	B'11111111'	; 1F-->'idle'

;--------------------------------------------------------
ERR_SLEEP
	NOOP
ERR_START
ERR_RANGE
ERR_HALF1
ERR_OVER
ERR_DATA
	SLEEP
	NOOP

	MOVF	ERR_NO,W
	CALL	BLINK

	MOVF	RX_N0,W
	CALL	BLINK

	MOVF	RX_N1,W
	CALL	BLINK

	MOVF	RX_N2,W
	CALL	BLINK

	MOVF	RX_N,W
	GOTO	BLINK_SLEEP

;--------------------------------------------------------
BLINK_SLEEP
		NOOP
		CALL	BLINK
		SLEEP
;--------------------------------------------------------
BLINK					; Wの値を表示、RxEをBN_N+1回点滅
		NOOP
		MOVWF	OUTDATA		; Wの値をOUTDATAに入れ
		INCF	BL_CN,F
		MOVF	BL_CN,W
		MOVWF	LINE

		SWAPF	OUTDATA,F	; OUTDATAのHighニブルを
		CALL	OUT_4B		; 表示する
BL_LOOP
		BSF	GPIO,RXE	; LED ON(RxEを0.25秒で点滅)
		CALL	WAIT_250MS	; wait 250ms
		BCF	GPIO,RXE	; LED OFF
		CALL	WAIT_250MS	; wait 250ms

		DECFSZ	LINE,F		; BL_N+1回点滅させる
		GOTO	BL_LOOP

		SWAPF	OUTDATA,F	; OUTDATAのLowニブルを
		CALL	OUT_4B		; 表示する

		BSF	GPIO,RXE	; LED ON(RxEを0.5秒点灯)
		CALL	WAIT_250MS	; wait 250ms
		CALL	WAIT_250MS	; wait 250ms
BLINK_OFF
		CLRF	OUTDATA		; 全てのLEDをOFFする
		CALL	OUT_4B		;
		BCF	GPIO,RXE	; RxEもOFFする

		RETLW	0

;--------------------------------
WAIT_250MS

	MOVLW	250		;1001*250+5+1US=250.256MS

WAIT_MS

	MOVWF	WAIT_CN		;1001*CN+5 US

WAIT_MS0

	MOVLW	249		;1;249
	MOVWF	WAIT_CN2	;1;

WAIT_MS1

	NOP			;1;
	DECFSZ	WAIT_CN2,1	;1;
	GOTO	WAIT_MS1	;2;4usx249

	DECFSZ	WAIT_CN,1	;1;
	GOTO	WAIT_MS0	;2;WAIT_CN*1001us

	RETLW	0	;CALL(2)+(1)+(5+4*249)*CN+RET(2)=1001*CN+5US

;--------------------------------

WAIT_US

	MOVWF	WAIT_CN		;1;

WAIT_US0

	GOTO	$+1		;2;
	DECFSZ	WAIT_CN,1	;1;
	GOTO	WAIT_US0	;2;WAIT_CN*5us

WAIT_4US
RET_POINT

	RETLW	0	;2;CALL(2)+(1)+(5)*CN+RET(2)=5*CN+5US

;--------------------------------------------------------
;--------------------------------------------------------
	ORG	0x100
;--------------------------------------------------------
SETUPPORTS

	NOOP
	MOVLW	OPT_INI		;WAKE-UP,PULL-UP,1:16 WDT PRESCALER
	OPTION

	MOVLW	GPIODIR		;Set I/O direction
	TRIS	GPIO

	MOVLW	GPIOINI		;Init I/O data
	MOVWF	GPIO

	CLRF	RX_N0
	CLRF	RX_N1
	CLRF	RX_N2
	CLRF	BL_CN
	CLRF	ERR_NO

CHK_WAKEUP

	BTFSC	STATUS,GPWUF
	GOTO	MAIN		;GP Wake-UpならばMAINへ

	CLRF	BL_CN
	MOVLW	H'A5'		;それ以外はTEST-SLEEP
	GOTO	BLINK_SLEEP

;--------------------------------------------------------
WRISE
	INCFSZ	RXCOUNT,F
	GOTO	$+2
	GOTO	ERR_OVER
	BTFSS	GPIO,RXD
	GOTO	WRISE		; 立上がり
	BTFSS	GPIO,RXD
	GOTO	WRISE
	BTFSS	GPIO,RXD
	GOTO	WRISE
	BSF	GPIOX,RXD
	BSF	GPIO,RXE	; LED ON
	RETLW	0
WFALL
	INCFSZ	RXCOUNT,F
	GOTO	$+2
	GOTO	ERR_OVER
	BTFSC	GPIO,RXD
	GOTO	WFALL
	BTFSC	GPIO,RXD
	GOTO	WFALL
	BTFSC	GPIO,RXD
	GOTO	WFALL
	BCF	GPIOX,RXD
	BCF	GPIO,RXE	; LED OFF
	RETLW	0

;--------------------------------------------------------
R1B_WRISE
	DECFSZ	RXCOUNT,F
	GOTO	$+3
	BCF	STATUS,C
	RETLW	0		;終了(READ_1B用に0を返す)
	BTFSS	GPIO,RXD
	GOTO	R1B_WRISE
	BTFSS	GPIO,RXD
	GOTO	R1B_WRISE
	BTFSS	GPIO,RXD
	GOTO	R1B_WRISE
	BSF	GPIOX,RXD
	BSF	GPIO,RXE	; LED ON
	GOTO	WAIT_HALF
R1B_WFALL
	DECFSZ	RXCOUNT,F
	GOTO	$+3
	BCF	STATUS,C
	RETLW	0		;終了(READ_1B用に0を返す)
	BTFSC	GPIO,RXD
	GOTO	R1B_WFALL
	BTFSC	GPIO,RXD
	GOTO	R1B_WFALL
	BTFSC	GPIO,RXD
	GOTO	R1B_WFALL
	BCF	GPIOX,RXD
	BCF	GPIO,RXE	; LED OFF
	GOTO	WAIT_HALF

;--------------------------------------------------------
WH_WRISE
	DECFSZ	RXCOUNT,F
	GOTO	$+3
	BSF	STATUS,C
	RETLW	1		;終了(READ_1B用に1を返す)
	BTFSS	GPIO,RXD
	GOTO	WH_WRISE
	BTFSS	GPIO,RXD
	GOTO	WH_WRISE
	BTFSS	GPIO,RXD
	GOTO	WH_WRISE
	BSF	GPIOX,RXD
	BSF	GPIO,RXE	; LED ON
	GOTO	YAPPA_0		;RXが反転したら0
WH_WFALL
	DECFSZ	RXCOUNT,F
	GOTO	$+3
	BSF	STATUS,C
	RETLW	1		;終了(READ_1B用に1を返す)
	BTFSC	GPIO,RXD
	GOTO	WH_WFALL
	BTFSC	GPIO,RXD
	GOTO	WH_WFALL
	BTFSC	GPIO,RXD
	GOTO	WH_WFALL
	BCF	GPIOX,RXD
	BCF	GPIO,RXE	; LED OFF
	GOTO	YAPPA_0		;RXが反転したら0

YAPPA_0
	DECFSZ	RXCOUNT,F
	GOTO	$+3
	BCF	STATUS,C
	RETLW	0		;終了(READ_1B用に0を返す)
	GOTO	YAPPA_0
;--------------------------------------------------------

	END

;--------------------------------------------------------
	ORG	0x3FF
;--------------------------------------------------------
	SLEEP