;***************************************************************************** ; ; 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 ;***************************************************************************** ; 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