;
; ctrlir.asm
;
TITLE	"IR Remote Receiver"
;
	include "reg84.h"
;
; To Do
;	Reject notches by accumulating very small counts
;

;
; Equates
;
DbgIr		equ	01H
;
; Port B bits(Bits 6 & 7 are used by serial comms.)
InpBit		equ	00H
;
if DbgIr != 0
DbgBit		equ	01H
DbgByte		equ	02H
DbgMain		equ	03H
DbgIsr		equ	04H
endif
;
; Flag bits in StatByte
PrevBit		equ	00H	; State of previous bit sample
RcvBusy		equ	01H	; In the process of receiving
RcvReady	equ	02H	; Code completely received
BitReady	equ	03h	; Bit ready for assembly
XmitCod1	equ	04h	; LSB of code ready for xmit
XmitCod2	equ	05h	; MSB of code ready for xmit
CodeOrun	equ	06h	; Code overrun
;
; Rate of interrupt. Set at 9600/sec. Rest of this arrived at by
; trial and error(mostly error). The calculation is as follows.
;   Required rate = 1/9600 = 104us
;   No. of instruction cycles = 104*3.58/4 
;                             = 93(with a crystal of 3.58 MHz)
;   RtccCount = 0 - 93 = A3h
;   With an overhead(12d?) it is afh 
;
RtccCount	equ	0afh
;
; Timing constants
HdrTic		equ	19h	; 2500us-2700us
MarkTic		equ	0bh	; 1100us-1300us
SpacTic		equ	05h	; 500us-700us
EndCode		equ	14h	; Gap indicating end of code(2ms)
;
; Data
	ORG	0CH	; Data Origin
;
SaveWReg	RES	1	; Save W during ISR
SaveStat	RES	1	; Save Status during ISR
IntDiv		RES	1	; Divider for periodic interrupt
;
CurrTime	RES	1	; The current ticks
MarkTime	RES	1	; Ticks the mark lasted
SpacTime	RES	1	; Ticks the space lasted
StatByte	RES	1	; A whole bunch of flags
CodeByt1	RES	1	; LSB of the received byte
CodeByt2	RES	1	; MSB of the received byte
CodeCnt		RES	1	; Bits received so far
SendCod1	RES	1	; Hold before transmit
SendCod2	RES	1
;
	include "ctrlcomm.h"
;
; Macros
;
;
; ChkRng : If value in register ChkReg is not between ChkVal
;          and ChkVal + 2 go to ErrLbl
ChkRng	MACRO	ChkReg,ErrLbl,ChkVal
	movf	ChkReg,0
	sublw	ChkVal+2
	JMPC	ErrLbl			; Value more than ChkVal + 2
	sublw	3
	JMPC	ErrLbl			; Value less than ChkVal
	ENDM
;
; Program
;
	ORG	0	; Reset vector
;
	goto	main
;
;
	ORG	4	; Interrupt vector
;
	goto	isr
;
;
	ORG	8	; Program starts here
;
main	call	InitIr
	call	InitPort
	if DbgIr != 0
	bcf	RegPortb,DbgBit
	bcf	RegPortb,DbgByte
	endif
loop1	btfss	StatByte,BitReady
	goto	DoneIr			; Nothing on IR port
	if DbgIr != 0
	bcf	RegPortb,DbgBit
	endif
	bcf	StatByte,BitReady
	movf	CodeCnt,0
	JMPNZ	NotHdr			; Not expecting header
	; Header has m=21-23, space=5-7
	ChkRng	MarkTime,IrErr,15h
	ChkRng	SpacTime,IrErr,5	; Expecting header & this ain't it
	incf	CodeCnt,1
	clrf	CodeByt1
	clrf	CodeByt2
	goto	DoneIr
NotHdr	bcf	RegStatus,BitCarry	; It's not the header
	rlf	CodeByt1,1
	rlf	CodeByt2,1
	ChkRng	MarkTime,NotOne,0bh	; Is it a 1?
	goto	Maybe1
NotOne	ChkRng	MarkTime,IrErr,5	; Is it a 0?
	goto	ChkSpac
Maybe1	bsf	CodeByt1,0
ChkSpac	ChkRng	SpacTime,IsItEnd,5
	incf	CodeCnt,1
	goto	DoneIr
IsItEnd	movf	SpacTime,0
	sublw	EndCode
	JMPNZ	IrErr
	clrf	CodeCnt
	movf	StatByte
	andlw	(1<<XmitCod1)|(1<<XmitCod2)
	JMPZ	NoOrun
	bsf	StatByte,CodeOrun
	goto	DoneIr
NoOrun	movf	CodeByt1,0
	movwf	SendCod1
	movf	CodeByt2,0
	movwf	SendCod2
	bsf	StatByte,XmitCod1
	bsf	StatByte,XmitCod2
	goto	DoneIr
IrErr	clrf	CodeCnt
	bcf	StatByte,RcvBusy
	if DbgIr != 0
	bcf	RegPortb,DbgByte
	endif
	goto	DoneIr
DoneIr	btfss	StatByte,XmitCod1
	goto	TryCod2
	btfss	P1Stat,TxRdy
	goto	SkipXmt
	movf	SendCod1,0
	bsf	RegPortb,DbgMain
	call	P1PutC
	bcf	RegPortb,DbgMain
	bcf	StatByte,XmitCod1
	goto	SkipXmt
TryCod2	btfss	StatByte,XmitCod2
	goto	SkipXmt
	btfss	P1Stat,TxRdy
	goto	SkipXmt
	movf	SendCod2,0
	bsf	RegPortb,DbgMain
	call	P1PutC
	bcf	RegPortb,DbgMain
	bcf	StatByte,XmitCod2
	goto	SkipXmt
SkipXmt	goto	loop1			; Play it again, Sam.
;
InitIr	bsf	RegStatus,BitRp0
	movlw	0
	movwf	RegTrisa		; Set Port A to all outputs
	movlw	1
	movwf	RegTrisb		; Set Port B Bit 0 to input
	bcf	RegOption,BitRts	; Enable RTCC timer
	bcf	RegStatus,BitRp0
	movlw	0
	movwf	RegRtcc
	movlw	0
	movwf	IntDiv
	movwf	CurrTime
	movwf	CodeByt1
	movwf	CodeByt2
	movwf	CodeCnt
	movwf	StatByte
	return
;
isr	btfss	RegIntcon,BitRtif
	retfie
	movwf	SaveWReg
	swapf	RegStatus,w
	movwf	SaveStat
	movlw	RtccCount
	movwf	RegRtcc
	bcf	RegStatus,BitRp0
	bsf	RegPorta,0
; The real work starts here
; The IR detector output is inverted. Thus, all tests of
; InpBit are inverted.
	btfsc	RegPortb,InpBit
	goto	IsSpace
	btfsc	StatByte,RcvBusy
	goto	Rcving			; In the middle of receiving
	bsf	StatByte,RcvBusy	; Now I'm busy
	if DbgIr != 0
	bsf	RegPortb,DbgByte
	endif
	clrf	CurrTime		; Start count
	goto	OneDone
	;
Rcving	btfss	StatByte,PrevBit
	goto	WasZero
	incf	CurrTime
	goto	OneDone
	;
WasZero	movf	CurrTime,0
	movwf	SpacTime
	clrf	CurrTime
	bsf	StatByte, BitReady
	if DbgIr != 0
	bsf	RegPortb,DbgBit
	endif
	goto	OneDone
	;
IsSpace	btfss	StatByte,RcvBusy
	goto	Done			; All quiet
	btfsc	StatByte,PrevBit	; Current input is space
	goto	WasOne
	incf	CurrTime
	movlw	EndCode
	subwf	CurrTime,0
	JMPNZ	Done
	movf	CurrTime,0		; Received end of code
	movwf	SpacTime
	bcf	StatByte,RcvBusy
	if DbgIr != 0
	bcf	RegPortb,DbgByte
	endif
	bsf	StatByte,BitReady
	if DbgIr != 0
	bsf	RegPortb,DbgBit
	endif
	goto	Done
;
WasOne	movf	CurrTime,0		; Just changed from 1 to 0
	movwf	MarkTime		; Store MarkTime
	clrf	CurrTime
	bcf	StatByte,PrevBit
	goto	Done
;
OneDone	bsf	StatByte,PrevBit
Done	incf	IntDiv,1
	movf	IntDiv,0
	sublw	2
	JMPNZ	SkipSer		; Call the serial ISR every two tics
	clrf	IntDiv
	call	SerIsr
SkipSer	bcf	RegPorta,0
	swapf	SaveStat,w
	movwf	RegStatus
	swapf	SaveWReg
	swapf	SaveWReg,w
	bcf	RegIntcon,BitRtif
	retfie
;
	include "ctrlcomm.asm"
;
	end

External Labels :

InitPort in ctrlcomm.asm
ChkSpac
P1PutC in ctrlcomm.asm
SerIsr in ctrlcomm.asm