;*****************************************************************************
;
; This program reads the Philips RC5 remote control codes from a
; handheld infrared remote control, and displays the decoded
; data words on an LCD.
;
; IMPORTANT NOTE: This code may be used for private purposes only.
; Anyone contemplating commercial use of this code should check
; with Philips Corporation for possible limitations and 
; restrictions on the use of their RC5 remote control code format.
;
; The interface works with a Hitachi HD66702- or 44780-based LC-Display
; This is a 2 line * 20 characters display module.
;
; Program       READRC5.ASM
; Last update   SEPT. 15, 1996
; Author        Brian Aase
; Thanks to Peer Ouwehand for the enhanced LCD driver routines and demo code.
;
;*****************************************************************************
;Mod info: This version assumes RA3 idling at high level so that Sharp
;IR pickup can be connected directly.
;
;*****************************************************************************
; Fosc          = 4MHz
; Cycle_time    = 1/Fosc / 4
;               = 1/(4*10^6) / 4
;               = 1uSec
;*****************************************************************************

	LIST P=16C84

	__CONFIG _CP_OFF & _WDT_OFF & _XT_OSC & _PWRTE_ON
	RADIX DEC

	include <c:\mplab\p16c84.inc>

;*****************************************************************************
; Equates, I/O, vars
;*****************************************************************************
RESET_V         EQU     0x0000          ; Address of RESET Vector
ISR_V           EQU     0x0004          ; Address of Interrupt Vector
OSC_FREQ        EQU     D'4000000'      ; Oscillator Frequency is 4 MHz

LCD_DATA        EQU     PORTB           ; LCD data lines interface
LCD_DATA_TRIS   EQU     TRISB
LCD_CTRL        EQU     PORTA           ; LCD control lines interface

LCD_LINE0       EQU     0x000
LCD_LINE1       EQU     0x040
LCD_LINE2       EQU     0x014
LCD_LINE3       EQU     0x054

; PORTA bits
DATA_IN		EQU     3               ; Input data from IR pickup
LCD_E           EQU     2               ; LCD Enable control line
LCD_RW          EQU     1               ; LCD Read/Write control line
LCD_RS          EQU     0               ; LCD Register-Select control line

; PORTB bits
DB7             EQU     7               ; LCD dataline 7 (MSB)
DB6             EQU     6               ; LCD dataline 6
DB5             EQU     5               ; LCD dataline 5
DB4             EQU     4               ; LCD dataline 4
DB3             EQU     3               ; LCD dataline 3
DB2             EQU     2               ; LCD dataline 2
DB1             EQU     1               ; LCD dataline 1
DB0             EQU     0               ; LCD dataline 0 (LSB)

; misc.

#DEFINE	RAMstart  0x0C			; For 16C84

LCD_TEMP        EQU     RAMstart        ; LCD subroutines internal use
COUNT           EQU     RAMstart+1      ; A counter, used multiple places
DELAY           EQU     RAMstart+2      ; Used in DELAYxxx routines
X_DELAY         EQU     RAMstart+3      ; Used in X_DELAYxxx routines
TOGGLE          EQU     RAMstart+4      ; The RC5 Toggle bit
SYSTEM          EQU     RAMstart+5      ; The RC5 System word
COMND           EQU     RAMstart+6      ; The RC5 Command word
MZDATA          EQU     RAMstart+7      ; The RC5 extra Data word
POINTER         EQU     RAMstart+8	; Used in TABLE_MSG subroutine
ASCII_O         EQU     RAMstart+9      ; ASCII One's digit to print
ASCII_T         EQU     RAMstart+10     ; ASCII Ten's digit to print
ASCII_H         EQU     RAMstart+11     ; ASCII Hundred's digit to print
BIT_COUNT	EQU	RAMstart+12	; Counter for incoming bits
BYTE_COUNT	EQU	RAMstart+13	; Counter for buffer bytes
TEMP		EQU	RAMstart+14	; Used by PARSE to hold the 2-bit pattern
SERIAL_BUF	EQU	RAMstart+15	; Buffer for incoming bitstream
SERIAL_2	EQU	RAMstart+16
SERIAL_3	EQU	RAMstart+17
SERIAL_4	EQU	RAMstart+18
SERIAL_5	EQU	RAMstart+19
SERIAL_6	EQU	RAMstart+20
FIELD		EQU	RAMstart+21	; Temporary storage for field bit
PARSE_FLAGS	EQU	RAMstart+22	; DECODE returns its data in this
MZ_FLAG		EQU	RAMstart+23	; Set if there is an extra data word
					; in the bitstream

w               EQU     0
f               EQU     1
ONE		EQU	B'00000001'	; 2-bit incoming pattern matches
ZERO		EQU	B'00000010'
;*****************************************************************************
; Program start
;*****************************************************************************
	ORG     RESET_V                 ; RESET vector location
RESET           GOTO    START


;*****************************************************************************
; This is the Periperal Interrupt routine. Should NOT get here
;*****************************************************************************
	ORG     ISR_V                   ; Interrupt vector location
INTERRUPT       BCF     STATUS, RP0     ; Select bank 0
		GOTO    INTERRUPT

;=============================================================================
; Table message to display: kept right up front to avoid crossing 0xff boundary
;=============================================================================

TABLE_ST        MOVWF   PCL
MSG_T           RETLW   'T'
		RETLW   'O'
		RETLW   'G'
		RETLW   ' '
		RETLW   '='
		RETLW   ' '
		RETLW   0x00
MSG_S           RETLW   'S'
		RETLW   'Y'
		RETLW   'S'
		RETLW   'T'
		RETLW   'E'
		RETLW   'M'
		RETLW   ' '
		RETLW   '='
		RETLW   ' '
		RETLW   0x00
MSG_C           RETLW   'C'
		RETLW   'O'
		RETLW   'M'
		RETLW   'N'
		RETLW   'D'
		RETLW   ' '
		RETLW   '='
		RETLW   ' '
		RETLW   0x00
MSG_D           RETLW   'D'
		RETLW   'A'
		RETLW   'T'
		RETLW   'A'
		RETLW   ' '
		RETLW   '='
		RETLW	0x00
MSG_Y		RETLW	'D'	; Place your debug messages here
		RETLW	'E'
		RETLW	'B'
		RETLW	'U'
		RETLW	'G'
		RETLW	' '
		RETLW	'M'
		RETLW	'E'
		RETLW	'S'
		RETLW	'S'
		RETLW	'A'
		RETLW	'G'
		RETLW	'E'
		RETLW	' '
		RETLW	'1'
		RETLW	0x00
MSG_Z		RETLW	'M'
		RETLW	'E'
		RETLW	'S'
		RETLW	'S'
		RETLW	'A'
		RETLW	'G'
		RETLW	'E'
		RETLW	' '
		RETLW	'2'
TABLE_END	RETLW	0X00
		
	IF ( (TABLE_ST & 0x0FF) >= (TABLE_END & 0x0FF) )
		MESSG   "Warning - Message table 'TABLE_ST' crosses page boundary"
	ENDIF

;*****************************************************************************
; Initialize processor registers
;*****************************************************************************
START                                   ; POWER_ON Reset (Beginning of program)
		CLRF    STATUS          ; Do initialization, Select bank 0
		CLRF    INTCON          ; Clear int-flags, Disable interrupts
		CLRF    PCLATH          ; Keep in lower 2KByte

		CLRF    PORTA           ; ALL PORT outputs should output Low.
		CLRF    PORTB
		BSF     STATUS, RP0     ; Select bank 1
		MOVLW   0x0F8           ; RA2-0 outputs, RA5-3 inputs
		MOVWF   TRISA
		MOVLW   0x000           ; RB7-0 outputs
		MOVWF   TRISB
		MOVLW	B'11010001'	; Option register setup:
					; No RB Pullups
					; TMR0 fed from internal clock
					; Assign prescaler to TMR0
					; use 1:4 prescaling ratio
		MOVWF	OPTION_REG

		BCF     STATUS, RP0     ; Select bank 0
		CALL    LCDINIT         ; Initialize LCDisplay

;***************************************************************************
; Look at RA4 to invoke debug routine instead of regular program
;***************************************************************************

POLL_RA4	BTFSS	PORTA,4
		GOTO	POLL_RA3	; Start regular program if RA4 is low
		MOVLW	LCD_LINE0	;
		CALL	LCDSDDA		; Set to first line
		MOVLW	MSG_Y		; Point to first message string
		CALL	TABLE_MSG	; Display it
		MOVLW	LCD_LINE1	;
		CALL	LCDSDDA		; Set to second line
		MOVLW	MSG_Z		; Point to next message string
		CALL	TABLE_MSG	; Display it
DB_LOOP		GOTO	DB_LOOP		; Hang here forever

;***************************************************************************
; When power is applied, wait for data on RA3 before proceeding
; Remember the Sharp IR pickup has an active-low output
;***************************************************************************

POLL_RA3	BTFSC	PORTA, DATA_IN	; The pickup module has inverse logic
		GOTO    POLL_RA3	; High is idle, low is active
		MOVLW	D'147'		; Found data, load timer for 444 uS
					; minus 11 overhead = 433 cycles
					; 255-147=108 counts x 4 prescaled
		MOVWF	TMR0		; Which creates a 1/4 bit wait
		BCF	INTCON,T0IF	; Clear timer overflow flag
		BCF	STATUS,C	; Initialize the carry flag
		CALL	READ		; Read the data stream
		BTFSC	STATUS,C	; Carry flag set means bogus data
		GOTO	POLL_RA3	; Start over if bad datastream
		CALL	PARSE		; Parse the data
		BTFSC	STATUS,C	; Carry flag set means no success
		GOTO	POLL_RA3	; Start over if bad parse
		
;*****************************************************************************
; Display the data we gathered
; 
;*****************************************************************************

		MOVLW   LCD_LINE0       ; 0x00
		CALL    LCDSDDA         ; Position cursor leftmost on first line
		MOVLW   MSG_T           ; Point to Toggle message
		CALL    TABLE_MSG       ; Display message
		MOVF    TOGGLE,w        ; Load the toggle bit to display
		MOVWF   ASCII_O         ; Send data to conversion routine
		CALL    HEX_TO_ASC      ; Convert to ascii
		MOVF    ASCII_O,w       ; Ones digit is in ascii_o
		CALL    LCDPUTCHAR      ; Display toggle data
		MOVLW   LCD_LINE0 + 0x009       ; Position 9
		CALL    LCDSDDA         ; Position cursor
		MOVLW   MSG_S           ; Point to System message
		CALL    TABLE_MSG       ; Display message
		MOVF    SYSTEM,w        ; Load the system byte to display
		MOVWF   ASCII_O         ; Send data to conversion routine
		CALL    HEX_TO_ASC      ; Convert to ascii
		MOVF    ASCII_T,w       ; Tens digit
		CALL    LCDPUTCHAR      ; Display it
		MOVF    ASCII_O,w       ; Ones digit
		CALL    LCDPUTCHAR      ; Display it

		MOVLW   LCD_LINE1       ; 0x40
		CALL    LCDSDDA         ; Set cursor leftmost on line 2
		MOVLW   MSG_C           ; Point to Command message
		CALL    TABLE_MSG       ; Display the message
		MOVF    COMND,w         ; Load the command byte to display
		MOVWF   ASCII_O         ; Send data to conversion routine
		CALL    HEX_TO_ASC      ; Convert to ascii
		MOVF    ASCII_H,w       ; Hundreds digit
		CALL    LCDPUTCHAR      ; Display it
		MOVF    ASCII_T,w       ; Tens digit
		CALL    LCDPUTCHAR      ; Display it
		MOVF    ASCII_O,w       ; Ones digit
		CALL    LCDPUTCHAR      ; Display it
		MOVLW   LCD_LINE1 + 0x00C       ; Position 12
		CALL    LCDSDDA         ; Set cursor
		MOVLW   MSG_D           ; Point to Data message
		CALL    TABLE_MSG       ; Display the message
		
		BTFSS	MZ_FLAG,0	; Is there a data word to display?
		GOTO	NO_DATA		; No
		
		MOVF    MZDATA,w        ; Load the data byte to display
		MOVWF   ASCII_O         ; Send data to conversion routine
		CALL    HEX_TO_ASC      ; Convert to ascii
		MOVF    ASCII_T,w       ; Tens digit
		CALL    LCDPUTCHAR      ; Display it
		MOVF    ASCII_O,w       ; Ones digit
		CALL    LCDPUTCHAR      ; Display it
		GOTO	POLL_RA3	; Wait for incoming again
		
NO_DATA		MOVLW	' '		; Print spaces instead of data value
		CALL	LCDPUTCHAR
		MOVLW	' '
		CALL	LCDPUTCHAR
		GOTO	POLL_RA3	; Wait for incoming again

;*****************************************************************************
; Main program ends here
; 
;*****************************************************************************


;*****************************************************************************
; Send a message using a table to output the message
; 
;*****************************************************************************
TABLE_MSG
		MOVWF   POINTER         ; Point to the first char. we want
LOOP_WR         
		MOVFW   POINTER         ; Renew the pointer
		CALL    TABLE_ST        ; Initiate table lookup
		XORLW   0x00            ; Is this the terminating char?
		BTFSC   STATUS,Z
		GOTO    END_WR
		CALL    LCDPUTCHAR      ; Actually put the char on display
		INCF    POINTER,f	; Point to the next char
		GOTO    LOOP_WR         ; Do next character
END_WR
		RETURN

;*****************************************************************************
; LCD Module Subroutines
;*****************************************************************************
;
;=============================================================================
; LCDINIT
; Initilize LC-Display Module
; This code sets up the Optrex DMC50218 (2x20)
;=============================================================================

LCDINIT
					; Busy-flag is not yet valid
		CLRF    LCD_CTRL        ; ALL PORT output should output Low.
					; power-up delay
		MOVLW   0x01E
		CALL    X_DELAY500      ; 30 * 0.5mS = 15mS
					; Busy Flag should be valid from here
		MOVLW   0x038           ; 8-bit-interface, 2-lines
		CALL    LCDPUTCMD
		MOVLW   0x000           ; disp.off, curs.off, no-blink
		CALL    LCDDMODE
		CALL    LCDCLEAR
		MOVLW   0x004           ; disp.on, curs.off
		CALL    LCDDMODE
		MOVLW   0x002           ; auto-inc (shift-cursor)
		CALL    LCDEMODE
		RETURN
;=============================================================================
; LCD_ENABLE
; Pulses LCD enable pin
;=============================================================================
LCD_ENABLE
		BSF     LCD_CTRL, LCD_E ; LCD E-line High
		BCF     LCD_CTRL, LCD_E ; LCD E-line Low
		RETURN
;=============================================================================
; LCDBUSY
; Returns when LCD busy-flag is inactive
;=============================================================================
LCDBUSY
		BSF     STATUS,RP0      ; Select Register page 1
		MOVLW   0x0FF           ; Set PORTB for input
		MOVWF   LCD_DATA_TRIS
		BCF     STATUS, RP0     ; Select Register page 0
		BCF     LCD_CTRL, LCD_RS; Set LCD for command mode
		BSF     LCD_CTRL, LCD_RW; Setup to read busy flag
		BSF     LCD_CTRL, LCD_E ; LCD E-line High
		MOVF    LCD_DATA, W     ; Read busy flag + DDram address
		BCF     LCD_CTRL, LCD_E ; LCD E-line Low
		ANDLW   0x80            ; Check Busy flag, High = Busy
		BTFSS   STATUS, Z
		GOTO    LCDBUSY
LCDNOTBUSY      BCF     LCD_CTRL, LCD_RW
		BSF     STATUS, RP0     ; Select Register page 1
		MOVLW   0x000
		MOVWF   LCD_DATA_TRIS   ; Set PORTB for output
		BCF     STATUS, RP0     ; Select Register page 0
		RETURN
;=============================================================================
; LCDCLEAR
; Clears display and returns cursor to home position (upper-left corner).
;=============================================================================
LCDCLEAR
		MOVLW   0x001
		CALL    LCDPUTCMD
		RETURN
;=============================================================================
; LCDHOME
; Returns cursor to home position.
; Returns display to original position (when shifted).
;=============================================================================
LCDHOME
		MOVLW   0x002
		CALL    LCDPUTCMD
		RETURN
;=============================================================================
; LCDEMODE
; Sets entry mode of display.
; Required entry mode must be set in W
;  b0   : 0 = no display shift  1 = display shift
;  b1   : 0 = auto-decrement    1 = auto-increment
;  b2-7 : don't care
;=============================================================================
LCDEMODE
		ANDLW   0x003           ; Strip upper bits
		IORLW   0x004           ; Function set
		CALL    LCDPUTCMD
		RETURN
;=============================================================================
; LCDDMODE
; Sets display control.
; Required display mode must be set in W
;  b0   : 0 = cursor blink off  1 = cursor blink on
;  b1   : 0 = cursor off        1 = cursor on
;  b2   : 0 = display off       1 = display on (display data remains in DDRAM)
;  b3-7 : don't care
;=============================================================================
LCDDMODE
		ANDLW   0x007           ; Strip upper bits
		IORLW   0x008           ; Function set
		CALL    LCDPUTCMD
		RETURN
;=============================================================================
; LCDSCGA
; Sets Character-Generator-RAM address. CGRAM is read/written after
;  this setting.
; Required CGRAM address must be set in W
;  b0-5 : required CGRAM address
;  b6-7 : don't care
;=============================================================================
LCDSCGA
		ANDLW   0x03F           ; Strip upper bits
		IORLW   0x040           ; Function set
		CALL    LCDPUTCMD
		RETURN
;=============================================================================
; LCDSDDA
; Sets the Display-Data-RAM address. DDRAM data is read/written after
;  this setting.
; Required DDRAM address must be set in W
;  b0-6 : required DDRAM address
;  b7   : don't care
;=============================================================================
LCDSDDA
		IORLW   0x080           ; Function set
		CALL    LCDPUTCMD
		RETURN
;=============================================================================
; LCDGADDR
; Returns address counter contents, used for both DDRAM and CGRAM.
; RAM address is returned in W
;=============================================================================
LCDGADDR
		BSF     STATUS,RP0      ; Select Register page 1
		MOVLW   0x0FF           ; Set PORTB for input
		MOVWF   LCD_DATA_TRIS
		BCF     STATUS, RP0     ; Select Register page 0
		BCF     LCD_CTRL, LCD_RS; Set LCD for command mode
		BSF     LCD_CTRL, LCD_RW; Setup to read busy flag
		BSF     LCD_CTRL, LCD_E ; LCD E-line High
		MOVF    LCD_DATA, W     ; Read busy flag + RAM address
		BCF     LCD_CTRL, LCD_E ; LCD E-line Low
		ANDLW   0x07F           ; Strip upper bit
		BCF     LCD_CTRL, LCD_RW
		BSF     STATUS, RP0     ; Select Register page 1
		MOVLW   0x000
		MOVWF   LCD_DATA_TRIS   ; Set PORTB for output
		BCF     STATUS, RP0     ; Select Register page 0
		RETURN
;=============================================================================
; LCDPUTCHAR
; Sends character to LCD
; Required character must be in W
;=============================================================================
LCDPUTCHAR
		MOVWF   LCD_TEMP        ; Character to be sent is in W
		CALL    LCDBUSY         ; Wait for LCD to be ready
		BCF     LCD_CTRL, LCD_RW; Set LCD in read mode
		BSF     LCD_CTRL, LCD_RS; Set LCD in data mode
		BSF     LCD_CTRL, LCD_E ; LCD E-line High
		MOVF    LCD_TEMP, W
		MOVWF   LCD_DATA        ; Send data to LCD
		BCF     LCD_CTRL, LCD_E ; LCD E-line Low
		RETURN
;=============================================================================
; LCDPUTCMD
; Sends command to LCD
; Required command must be in W
;=============================================================================
LCDPUTCMD
		MOVWF   LCD_TEMP        ; Command to be sent is in W
		CALL    LCDBUSY         ; Wait for LCD to be ready
		BCF     LCD_CTRL, LCD_RW; Set LCD in read mode
		BCF     LCD_CTRL, LCD_RS; Set LCD in command mode
		BSF     LCD_CTRL, LCD_E ; LCD E-line High
		MOVF    LCD_TEMP, W
		MOVWF   LCD_DATA        ; Send data to LCD
		BCF     LCD_CTRL, LCD_E ; LCD E-line Low
		RETURN

;*****************************************************************************
; Delay_time    = ((DELAY_value * 3) + 4) * Cycle_time
; DELAY_value   = (Delay_time - (4 * Cycle_time)) / (3 * Cycle_time)
;
; i.e. (@ 4MHz crystal)
; Delay_time    = ((32 * 3) + 4) * 1uSec
;               = 100uSec
; DELAY_value   = (500uSec - 4) / 3
;               = 165.33
;               = 165
;*****************************************************************************
DELAY500        MOVLW   D'165'          ; +1            1 cycle
		MOVWF   DELAY           ; +2            1 cycle
DELAY500_LOOP   DECFSZ  DELAY, F        ; step 1        1 cycle
		GOTO    DELAY500_LOOP   ; step 2        2 cycles
DELAY500_END    RETURN                  ; +3            2 cycles
;
;
X_DELAY500      MOVWF   X_DELAY         ; +1            1 cycle
X_DELAY500_LOOP CALL    DELAY500        ; step1         wait 500uSec
		DECFSZ  X_DELAY, F      ; step2         1 cycle
		GOTO    X_DELAY500_LOOP ; step3         2 cycles
X_DELAY500_END  RETURN                  ; +2            2 cycles

;===========================================================================
; HEX_TO_ASC .. Converts a hex digit to three ASCII characters
;               Enter with the hex digit in ASCII_O
;               Exit with Hundreds ascii digit in ASCII_H,
;               Tens ascii digit in ASCII_T,
;               and Ones ascii digit in ASCII_O.
;               The incoming byte is not preserved.
;===========================================================================

HEX_TO_ASC      MOVLW   '0'             ; Preload a zero into 10's & 100's
		MOVWF   ASCII_T
		MOVWF   ASCII_H
		
DO_100s         MOVLW   D'100'
		SUBWF   ASCII_O,w       ; Subtract 100 to test size
		BNC     DO_10s          ; It's less than 100, so branch
		
		MOVWF   ASCII_O         ; It was bigger, so decrement it
		INCF    ASCII_H,f       ; Bump up the 100's digit
		GOTO    DO_100s         ; Loop again till < 100
		
DO_10s          MOVLW   D'10'
		SUBWF   ASCII_O,w       ; Subtract 10 to test size
		BNC     ADJUST          ; It's less than 10, so branch
		
		MOVWF   ASCII_O         ; It was bigger, so decrement it
		INCF    ASCII_T,f       ; Bump up the 10's digit
		GOTO    DO_10s          ; Loop again till < 10
		
ADJUST          MOVLW   '0'             ; The # in ASCII_O is now < 10
		ADDWF   ASCII_O,f       ; Turn it into an ASCII character
		
BLANK_ZEROS     MOVLW   '0'
		XORWF   ASCII_H,w       ; Is the 100's char a 0?
		BTFSS   STATUS,Z
		GOTO    HEX_DONE        ; No.
		MOVLW   ' '             ; Yes,
		MOVWF   ASCII_H         ; So replace it with a space.
		
		MOVLW   '0'
		XORWF   ASCII_T,w       ; Is the 10's char a 0?
		BTFSS   STATUS,Z
		GOTO    HEX_DONE        ; No.
		MOVLW   ' '             ; Yes,
		MOVWF   ASCII_T         ; So replace it with a space

HEX_DONE        RETURN

;****************************************************************************
;READ .. reads the incoming data stream and saves it into a 6-byte buffer
;        Remember again that the IR pickup output is active low
;****************************************************************************

READ					; First, zero all storage
		MOVLW	SERIAL_BUF	; Start of RAM buffer
		MOVWF	FSR		; Load indirect register
RD_LOOP1	CLRF	INDF		; Zero out the buffer
		INCF	FSR,f		; Increment to next byte
		MOVLW	SERIAL_BUF+6	; Test for 6 bytes done
		XORWF	FSR,w
		BTFSS	STATUS,Z
		GOTO	RD_LOOP1	; Loop till done
		
		CLRF	BIT_COUNT
		CLRF	BYTE_COUNT
		MOVLW	SERIAL_BUF	; Reset the indirect pointer back
		MOVWF	FSR		; to the start of the buffer space

RD_LOOP2	BTFSS	INTCON, T0IF	; Wait till timer goes 444 Usec
		GOTO	RD_LOOP2
		CALL	RESTART_TMR0	; Reset timer for 888 uSec
		BTFSC	PORTA,3		; Check RA3 signal (i.e. debounce)
		GOTO	BAD_DATA	; If it's high (logic 0), bail out

RD_LOOP3	BTFSS	INTCON, T0IF	; Wait till timer goes 888 uSec
		GOTO	RD_LOOP3

		CALL	RESTART_TMR0	; Restart 888 uSec timer
		INCF	BIT_COUNT,f
		MOVLW	D'43'		; See if we've done all 42 bits
		XORWF	BIT_COUNT,w
		BTFSC	STATUS,Z
		GOTO	RD_FINISH	; We've done all 42 bits
		BSF	STATUS,C	; Preset the carry bit
		BTFSC	PORTA,3		; Test RA3
		BCF	STATUS,C	; Input bit is high (logic 0) so change carry
					; Now the input bit is in the carry flag
		RLF	INDF,f		; Rotate it into the buffer
		INCF	BYTE_COUNT,f	; 
		BTFSC	BYTE_COUNT,3	; See if all 8 bits are filled
		CALL	NEXT_BYTE	; Yes, use next buffer byte
		GOTO	RD_LOOP3	; Loop back & do the rest of the bits
		
RD_FINISH	BCF	STATUS,C	; 
		RLF	INDF,f		; The last buffer byte has only 2
		RLF	INDF,f		; bits loaded.
		RLF	INDF,f		; So we shift them up to the top
		RLF	INDF,f		; the hard way.
		RLF	INDF,f
		RLF	INDF,f
		BCF	STATUS,C	; Clear carry to show a successful capture
		
		RETURN

;****************************************************************************
;NEXT_BYTE .. Increment the FSR and clear the counter
;
;****************************************************************************

NEXT_BYTE	INCF	FSR,f
		CLRF	BYTE_COUNT
		RETURN

;****************************************************************************
;RESTART_TMR0 .. Restart the timer with an 888 uSec delay
;                and refresh the overflow flag.
;                MPLAB stopwatch says there is a loop overhead of
;                14 cycles, thus we need it to run for 888-14=874 uSec
;****************************************************************************

RESTART_TMR0
		BCF	INTCON, T0IF	; Clear the overflow flag
		MOVLW	D'37'		; 255 - 37 = 218 x 4 = 872 uSec
		MOVWF	TMR0
		RETURN

;****************************************************************************
;PARSE .. decodes the saved datastream into the various
;         RC5 words and flags.
;****************************************************************************

PARSE
		MOVLW	SERIAL_BUF	; Put buffer start address into
		MOVWF	FSR		; the FSR
		CLRF	TEMP		; Initialize variables
		CLRF	BYTE_COUNT	;
		CLRF	MZ_FLAG		;
		CLRF	FIELD		;
		CLRF	TOGGLE		;
		CLRF	SYSTEM		;
		CLRF	COMND		;
		CLRF	MZDATA		;
		
					; First one is the field bit
		RLF	INDF,f		; First bit into carry
		RLF	TEMP,f		; Put it into TEMP for decoding
		RLF	INDF,f		; Second bit
		RLF	TEMP,f		; ditto
		INCF	BYTE_COUNT,f	; Start keeping track of INDF bit position
		CALL	DECODE
		BTFSC	PARSE_FLAGS,2	; Illegal pattern found
		GOTO	BAD_DATA
					; Must have found a good bit
		RRF	PARSE_FLAGS,f	; Put field bit into Carry
		RLF	FIELD,f		; Move it into FIELD
				
		CLRF	TEMP		; Next one is the toggle bit
		RLF	INDF,f		; First bit into carry
		RLF	TEMP,f		; Put it into TEMP for decoding
		RLF	INDF,f		; Second bit
		RLF	TEMP,f		; ditto
		INCF	BYTE_COUNT,f	; keep track of INDF bit position
		CALL	DECODE
		BTFSC	PARSE_FLAGS,2	; Illegal pattern found
		GOTO	BAD_DATA
					; Must have found a good bit
		RRF	PARSE_FLAGS,f	; Put toggle bit into Carry
		RLF	TOGGLE,f	; Move it into TOGGLE
		

		MOVLW	0x05		; Next one is the system byte, 5 bits
		MOVWF	COUNT
SYS_1		CLRF	TEMP		
		RLF	INDF,f		; First bit into carry
		RLF	TEMP,f		; Put it into TEMP for decoding
		RLF	INDF,f		; Second bit
		RLF	TEMP,f		; ditto
		INCF	BYTE_COUNT,f	; keep track of INDF bit position
		BTFSC	BYTE_COUNT,2	; (there are two rotates per count)
		CALL	NEXT_BYTE	; Go to next buffer byte when count=4
		CALL	DECODE
		BTFSC	PARSE_FLAGS,2	; Illegal pattern found
		GOTO	BAD_DATA
					; Must have found a good bit
		RRF	PARSE_FLAGS,f	; Put bit into Carry
		RLF	SYSTEM,f	; Move it into SYSTEM
		DECFSZ	COUNT,f		; Have we done all 5 bits?
		GOTO	SYS_1		; No, not yet
		
					; Here we test for the two spaces
					; present in the extended data format.
					; If they appear, we set a flag
					; and skip over them to the command
					; word.
					
		MOVLW	B'11000000'	; Mask for 2nd buffer byte
		ANDWF	SERIAL_2,w	; Are the 2 top bits zero?
		BTFSS	STATUS,Z	;
		GOTO	PHILIPS		; No space found
		
		CALL	NEXT_BYTE	; Skip over first space
		RLF	INDF,f		; Skip over second space
		RLF	INDF,f		;
		INCF	BYTE_COUNT,f
		MOVLW	0x01
		MOVWF	MZ_FLAG		; Set the flag
	

PHILIPS		MOVLW	0x06		; Next one is the command byte, 6 bits
		MOVWF	COUNT
CMD_1		CLRF	TEMP		
		RLF	INDF,f		; First bit into carry
		RLF	TEMP,f		; Put it into TEMP for decoding
		RLF	INDF,f		; Second bit
		RLF	TEMP,f		; ditto
		INCF	BYTE_COUNT,f	; keep track of INDF bit position
		BTFSC	BYTE_COUNT,2	; (there are two rotates per count)
		CALL	NEXT_BYTE	; Go to next buffer byte when count=4
		CALL	DECODE
		BTFSC	PARSE_FLAGS,2	; Illegal pattern found
		GOTO	BAD_DATA
					; Must have found a good bit
		RRF	PARSE_FLAGS,f	; Put bit into Carry
		RLF	COMND,f		; Move it into COMND
		DECFSZ	COUNT,f		; Have we done all 6 bits?
		GOTO	CMD_1		; No, not yet
		
		BTFSS	FIELD,0		; If field bit=0 then comnd=comnd+64
		BSF	COMND,6
		
		BTFSC	MZ_FLAG,0	; Test for extra data word
		GOTO	MDAT_1		; Parse extra data word
		RETURN			; Finish here if Philips format
		
MDAT_1		MOVLW	0x06		; Next one is the data byte, 6 bits
		MOVWF	COUNT
MDAT_2		CLRF	TEMP		
		RLF	INDF,f		; First bit into carry
		RLF	TEMP,f		; Put it into TEMP for decoding
		RLF	INDF,f		; Second bit
		RLF	TEMP,f		; ditto
		INCF	BYTE_COUNT,f	; keep track of INDF bit position
		BTFSC	BYTE_COUNT,2	; (there are two rotates per count)
		CALL	NEXT_BYTE	; Go to next buffer byte when count=4
		CALL	DECODE
		BTFSC	PARSE_FLAGS,2	; Illegal pattern found
		GOTO	BAD_DATA
					; Must have found a good bit
		RRF	PARSE_FLAGS,f	; Put bit into Carry
		RLF	MZDATA,f	; Move it into MZDATA
		DECFSZ	COUNT,f		; Have we done all 6 bits?
		GOTO	MDAT_2		; No, not yet

PARSE_DONE	RETURN


;****************************************************************************
; DECODE .. enter with two-bit data in TEMP
;           return with result code in PARSE_FLAGS, thus
;           PARSE_FLAGS<0> = valid data, one or zero
;           PARSE_FLAGS<1> = unused
;           PARSE_FLAGS<2> = set if data is invalid
;****************************************************************************

DECODE
		CLRF	PARSE_FLAGS
		MOVLW	ONE
		XORWF	TEMP,w		; Compare to bit pattern '01'
		BZ	ONE_EXIT
		
		MOVLW	ZERO
		XORWF	TEMP,w		; compare to bit pattern '10'
		BZ	ZERO_EXIT
		
BAD_EXIT	BSF	PARSE_FLAGS,2	; bit pattern neither 01 nor 10
		RETURN
		
ONE_EXIT	BSF	PARSE_FLAGS,0	; Return with valid data in LSB
ZERO_EXIT	RETURN			; of the PARSE_FLAGS variable
		
;****************************************************************************
; BAD_DATA .. Exit if Read or Parse doesn't like what it finds.
;             The Carry flag is set as an error message.
;****************************************************************************

BAD_DATA
		BSF	STATUS,C	; Set the carry flag to show error
		RETURN
		
;****************************************************************************		

		END			; End of program

