;; Filename:	renard-595-192-20070831.asm
;; Copyright (c) 2007 Phil Short
;;
;;  This program is provided free for you to use in any way that you wish,
;;  subject to the laws and regulations where you are using it.  Due diligence
;;  is strongly suggested before using this code.
;;
;;  The Author makes no warranty of any kind, express or implied, with regard
;;  to this program or the documentation contained in this document.  The
;;  Author shall not be liable in any event for incidental or consequential
;;  damages in connection with, or arising out of, the furnishing, performance
;;  or use of these programs.
;;
;;  This file contains the firmware source code for a device that receives
;;  serial input and uses it to 'dim' a 64-channel 595 board (i.e. a Renard to
;;  595 converter).  This file supports 192 levels of dimming, and requires an
;;  external gate (HC02) to handle PWM support (so that the status LEDs on the
;;  595 board can behave as expected).
;;
;;  Revision history
;;  20070608 Started coding the Renard-595 adapter version.
;;  20070619 Changed from 100-level to 192-level operation.
;;  20070624 Fixed rx code related to daisy-chaining.
;;  20070823 Added firmware control for clearing the s/r when in PWM mode.
;;      This change requires a h/w change to the board - disconnect the ZC
;;      signal from pin 2 of the HC02, and connect pin 2 of the HC02 to pins
;;      11,12 of the PIC.
;;      Changed code so that no pulse is output when the dimmer input level is
;;      zero, so that the status LED on the 595 board goes totally off.
;;  20070824 Added some test code so that the auto-line frequency algorithm can
;;      be disabled.
;;  20070831 Made changes to keep output from occuring too close to the end of
;;      the cycle.
;;  20071211 PJS Fixed bug in code for handling an incoming PAD character
;;      (replaced 'goto mainloop' with return instruction).
;;
;;  Hardware mappings
;;  PIN 6  (RB0) - data output to 595
;;  PIN 7  (RB1) - uart_in
;;  PIN 8  (RB2) - uart_out
;;  PIN 9  (RB3) - clock output to 595 (uses PWM feature of CCP module)
;;  PIN 10 (RB4) - zero-crossing
;;  PIN 11 (RB5) - PWM_reset_feedback (input)
;;  PIN 12 (RB6) - PWM_reset (output)
;;  PIN 13 (RB7) - input only

;;  PIN 17 (RA0) - latch output to 595
;;  PIN 18 (RA1) - HEARTBEAT LED output
;;  PIN 1  (RA2) - Overrun_Error LED output
;;  PIN 2  (RA3) - Frame_Error LED output
;;  PIN 3  (RA4) - (pullup needed)
;;  PIN 4  (RA5) - 
;;  PIN 15 (RA6) - oscillator
;;  PIN 16 (RA7) - oscillator
;;
;;
;;  Timing budget:
;;  Time budgeted for each time slot (192 levels, 60 Hz) = 40 uS (or so)
;;  ISR - takes about 30 uS, leaving about 10 uS for foreground code
;;  Time to process each rx Char = <~ 50 instructions = 10 uS
;;  Interval between characters at 115200 baud = 86 uS (8N1)
;;  Interval between characters at 250000 baud = 44 uS (8N2)
;;
;;  This code fits in less than 2K of program space, and takes advantage of
;;  that fact at various places to save a few bytes of code, and so it may
;;  not necessarily work as intended if the code size is increased.
;; 
;;  This code does not make use of automatic baud-rate detection, nor does
;;  it use timer1, the low-power, wake-up, analog comparator or A/D
;;  capabilities of the chip.
;;
;;  The interrupt used by this program is timer0, which is intended to create
;;  a periodic clock.  Since the timer is reset inside of the ISR, the
;;  foreground routine should disable interrupts for as short a time as
;;  possible (or not at all, if this can be accomplished).

 LIST R=DEC
 INCLUDE "p16f627a.inc"

 __CONFIG _CP_OFF & _DATA_CP_OFF &_LVP_OFF & _BOREN_ON &_MCLRE_OFF & _PWRTE_ON & _WDT_OFF & _HS_OSC

;; Page 0 registers.

 CBLOCK 0x020
;; variables used by the interrupt service routine (ISR)

  ctr_ckt0, ctr_ckt1, ctr_ckt2, ctr_ckt3
  ctr_ckt4, ctr_ckt5, ctr_ckt6, ctr_ckt7	

  ctr_ckt8, ctr_ckt9, ctr_cktA, ctr_cktB
  ctr_cktC, ctr_cktD, ctr_cktE, ctr_cktF	

  ctr_ckt10, ctr_ckt11, ctr_ckt12, ctr_ckt13
  ctr_ckt14, ctr_ckt15, ctr_ckt16, ctr_ckt17	

  ctr_ckt18, ctr_ckt19, ctr_ckt1A, ctr_ckt1B
  ctr_ckt1C, ctr_ckt1D, ctr_ckt1E, ctr_ckt1F	

  ctr_ckt20, ctr_ckt21, ctr_ckt22, ctr_ckt23
  ctr_ckt24, ctr_ckt25, ctr_ckt26, ctr_ckt27	

  ctr_ckt28, ctr_ckt29, ctr_ckt2A, ctr_ckt2B
  ctr_ckt2C, ctr_ckt2D, ctr_ckt2E, ctr_ckt2F	

  ctr_ckt30, ctr_ckt31, ctr_ckt32, ctr_ckt33
  ctr_ckt34, ctr_ckt35, ctr_ckt36, ctr_ckt37	

  ctr_ckt38, ctr_ckt39, ctr_ckt3A, ctr_ckt3B
  ctr_ckt3C, ctr_ckt3D, ctr_ckt3E, ctr_ckt3F	
 ENDC	

;; registers that are visible across all 4 memory pages
 CBLOCK 0x70

  _w, _status, _fsr

  rx_state

  zc_delayline

  timer0_preset
  acc

  heartbeat
  frame_err_countdown
  overrun_countdown
  rxd_countdown
  porta_image

  isr_counter

 ENDC

;; Page 1 registers
 CBLOCK 0xA0
  slot_id_ckt0, slot_id_ckt1, slot_id_ckt2, slot_id_ckt3
  slot_id_ckt4, slot_id_ckt5, slot_id_ckt6, slot_id_ckt7	

  slot_id_ckt8, slot_id_ckt9, slot_id_cktA, slot_id_cktB
  slot_id_cktC, slot_id_cktD, slot_id_cktE, slot_id_cktF	

  slot_id_ckt10, slot_id_ckt11, slot_id_ckt12, slot_id_ckt13
  slot_id_ckt14, slot_id_ckt15, slot_id_ckt16, slot_id_ckt17	

  slot_id_ckt18, slot_id_ckt19, slot_id_ckt1A, slot_id_ckt1B
  slot_id_ckt1C, slot_id_ckt1D, slot_id_ckt1E, slot_id_ckt1F	

  slot_id_ckt20, slot_id_ckt21, slot_id_ckt22, slot_id_ckt23
  slot_id_ckt24, slot_id_ckt25, slot_id_ckt26, slot_id_ckt27	

  slot_id_ckt28, slot_id_ckt29, slot_id_ckt2A, slot_id_ckt2B
  slot_id_ckt2C, slot_id_ckt2D, slot_id_ckt2E, slot_id_ckt2F	

  slot_id_ckt30, slot_id_ckt31, slot_id_ckt32, slot_id_ckt33
  slot_id_ckt34, slot_id_ckt35, slot_id_ckt36, slot_id_ckt37	

  slot_id_ckt38, slot_id_ckt39, slot_id_ckt3A, slot_id_ckt3B
  slot_id_ckt3C, slot_id_ckt3D, slot_id_ckt3E, slot_id_ckt3F	

 ENDC
;; assigned numbers:

 #define CRYSTAL_FREQ 20000000
 #define BAUDRATE 57600
 #define DEFAULT_LINERATE 50
 #define MAX_LINERATE 70
 #define TEST_LINERATE 60

 #define TMR0_PRESCALE_FACTOR 1
 #define NBR_CELLS 192

 #define NULLED_CELLS 32
		
;; derived numbers:

 #define MIN_TMR1H ((CRYSTAL_FREQ/4)/MAX_LINERATE/NBR_CELLS/2/TMR0_PRESCALE_FACTOR)

;; This parameter (TMR_TWEAK) must be the ratio of two integers (don't add
;; parenthesis around this, it'll screw things up).  The ratio should be more
;; than 1, and less than 1.2 or 1.3.
	
 #define TMR_TWEAK 120/100
	
 #define TEST_TMR_CONST_NONE 2-((CRYSTAL_FREQ/4)/TEST_LINERATE/(NBR_CELLS * TMR_TWEAK)/2/TMR0_PRESCALE_FACTOR)
		 
;; initializer for baudrate generator (based on formula from datasheet),
;; slightly modified to do proper rounding.
	
 #define BAUD_INIT ((CRYSTAL_FREQ + BAUDRATE*8)/(BAUDRATE*16) - 1)

;; initializer for cell timer (replaces TIMER_MAGIC0/1).  This assumes 50 Hz,
;; but it will self-adjust after 1/2 AC cycle (better too slow than too fast).

 #define CELL_RATE ((2*DEFAULT_LINERATE) * 256)
 #define CELL_TMR_INIT (CRYSTAL_FREQ/4/CELL_RATE)

;; i/o bit definition

 #define ZC_BIT   4
 #define ZC       PORTB,(ZC_BIT)
 #define ZC_MASK  (1<<(ZC_BIT))

;;
 #define LATCH_EN PORTA,0

 #define TEST_STROBE PORTA,3

 #define DATA_595  PORTB,0

 #define HEARTBEAT_BIT  1
 #define HEARTBEAT_LED	PORTA, HEARTBEAT_BIT
 #define OVERRUN_BIT    2
 #define FRAME_ERR_BIT  3
 #define RXD_BIT 4

 #define END_PWM  PORTB,6	

;; ---------------------------------------------------------------------------

 org 0
	nop
	goto initialize

 org 4
	;; interrupt routine (for timer 0)
	;; about 150 instructions, or 30 uS, the interval between interrupts
	;; is around 40 uS (with 60 Hz AC power line frequency).
	;; The timing here is a bit delicate (ensuring that there are exactly
	;; 64 clocks to the 595, and that the data is properly aligned with
	;; the clock), so don't tweak the code in between the T2CON accesses
	;; unless you have access to an oscilloscope to verify that the timing
	;; is still correct.
Int
	movwf	_w		; save 'w'
	movf	STATUS, w	; src, dst
	movwf	_status		; save f to _status

	bcf	INTCON,T0IF	; reset the interrupt

	incf	isr_counter,f
	btfsc	STATUS,Z
	bsf	END_PWM
		
 #IFDEF TEST_TMR_CONST
        movlw	TEST_TMR_CONST
 ELSE
	movf	timer0_preset,w
 ENDIF

	addwf	TMR0,f
	
	movlw	1
	bcf	LATCH_EN
	
	addwf	ctr_ckt3F,f
	bsf	T2CON,TMR2ON	; start the clock.
	rlf	PORTB,f
	
	addwf	ctr_ckt3E,f
	rlf	PORTB,f
	
	addwf	ctr_ckt3D,f
	rlf	PORTB,f
	
	addwf	ctr_ckt3C,f
	rlf	PORTB,f
	
	addwf	ctr_ckt3B,f
	rlf	PORTB,f
	
	addwf	ctr_ckt3A,f
	rlf	PORTB,f
	
	addwf	ctr_ckt39,f
	rlf	PORTB,f
	
	addwf	ctr_ckt38,f
	rlf	PORTB,f
	

	addwf	ctr_ckt37,f
	rlf	PORTB,f
	
	addwf	ctr_ckt36,f
	rlf	PORTB,f
	
	addwf	ctr_ckt35,f
	rlf	PORTB,f
	
	addwf	ctr_ckt34,f
	rlf	PORTB,f
	
	addwf	ctr_ckt33,f
	rlf	PORTB,f
	
	addwf	ctr_ckt32,f
	rlf	PORTB,f
	
	addwf	ctr_ckt31,f
	rlf	PORTB,f
	
	addwf	ctr_ckt30,f
	rlf	PORTB,f
	
	addwf	ctr_ckt2F,f
	rlf	PORTB,f
	
	addwf	ctr_ckt2E,f
	rlf	PORTB,f
	
	addwf	ctr_ckt2D,f
	rlf	PORTB,f
	
	addwf	ctr_ckt2C,f
	rlf	PORTB,f
	
	addwf	ctr_ckt2B,f
	rlf	PORTB,f
	
	addwf	ctr_ckt2A,f
	rlf	PORTB,f
	
	addwf	ctr_ckt29,f
	rlf	PORTB,f
	
	addwf	ctr_ckt28,f
	rlf	PORTB,f
	

	addwf	ctr_ckt27,f
	rlf	PORTB,f
	
	addwf	ctr_ckt26,f
	rlf	PORTB,f
	
	addwf	ctr_ckt25,f
	rlf	PORTB,f
	
	addwf	ctr_ckt24,f
	rlf	PORTB,f
	
	addwf	ctr_ckt23,f
	rlf	PORTB,f
	
	addwf	ctr_ckt22,f
	rlf	PORTB,f
	
	addwf	ctr_ckt21,f
	rlf	PORTB,f
	
	addwf	ctr_ckt20,f
	rlf	PORTB,f
	
	addwf	ctr_ckt1F,f
	rlf	PORTB,f
	
	addwf	ctr_ckt1E,f
	rlf	PORTB,f
	
	addwf	ctr_ckt1D,f
	rlf	PORTB,f
	
	addwf	ctr_ckt1C,f
	rlf	PORTB,f
	
	addwf	ctr_ckt1B,f
	rlf	PORTB,f
	
	addwf	ctr_ckt1A,f
	rlf	PORTB,f
	
	addwf	ctr_ckt19,f
	rlf	PORTB,f
	
	addwf	ctr_ckt18,f
	rlf	PORTB,f
	

	addwf	ctr_ckt17,f
	rlf	PORTB,f
	
	addwf	ctr_ckt16,f
	rlf	PORTB,f
	
	addwf	ctr_ckt15,f
	rlf	PORTB,f
	
	addwf	ctr_ckt14,f
	rlf	PORTB,f
	
	addwf	ctr_ckt13,f
	rlf	PORTB,f
	
	addwf	ctr_ckt12,f
	rlf	PORTB,f
	
	addwf	ctr_ckt11,f
	rlf	PORTB,f
	
	addwf	ctr_ckt10,f
	rlf	PORTB,f
	
	addwf	ctr_cktF,f
	rlf	PORTB,f
	
	addwf	ctr_cktE,f
	rlf	PORTB,f
	
	addwf	ctr_cktD,f
	rlf	PORTB,f
	
	addwf	ctr_cktC,f
	rlf	PORTB,f
	
	addwf	ctr_cktB,f
	rlf	PORTB,f
	
	addwf	ctr_cktA,f
	rlf	PORTB,f
	
	addwf	ctr_ckt9,f
	rlf	PORTB,f
	
	addwf	ctr_ckt8,f
	rlf	PORTB,f
	

	addwf	ctr_ckt7,f
	rlf	PORTB,f
	
	addwf	ctr_ckt6,f
	rlf	PORTB,f
	
	addwf	ctr_ckt5,f
	rlf	PORTB,f
	
	addwf	ctr_ckt4,f
	rlf	PORTB,f
	
	addwf	ctr_ckt3,f
	rlf	PORTB,f
	
	addwf	ctr_ckt2,f
	rlf	PORTB,f
	
	addwf	ctr_ckt1,f
	rlf	PORTB,f
	
	addwf	ctr_ckt0,f
	rlf	PORTB,f
	
	bcf	T2CON,TMR2ON
	bsf	LATCH_EN
isr_exit:	

	movf	_status, w
	movwf	STATUS
	swapf	_w,f
	swapf	_w,w
	retfie

;;
;; End of Interrupt Routine, start of Foreground code.
;;

initialize:
;;
;; initialize memory.
;;
	movlw	slot_id_ckt0
	movwf	FSR
	bcf	STATUS,IRP
	movlw	64
init_clr_loop:	
	clrf	INDF
	bsf	INDF,6		; 0x40
	incf	FSR,f
	addlw	0xFF
	btfss	STATUS,Z
	goto	init_clr_loop

	movlw	0x4
	movwf	rx_state

	movlw	~CELL_TMR_INIT
	movwf	timer0_preset
	
;; configure the I/O ports

	bcf	STATUS,RP0	; select bank 0
	movlw	7
	movwf	CMCON		; disable the comparators

	movlw	0xE
	movwf	PORTA
	clrf	PORTB

	bsf	STATUS,RP0

	movlw	0xC0		; 
	movwf	TRISA ^ 0x80	;

	movlw	0xB6		; bits 0,3 are output to the 595/Grinch, bit 6
				; is output for clearing the shift register,
	movwf	TRISB ^ 0x80	; are all inputs.

;; configure the UART

	movlw	BAUD_INIT
	movwf	SPBRG ^ 0x80

	bcf	STATUS,RP0		

	movlw	1
	
	movlw	0x90		; enable serial port with continuous 8-bit rx
	movwf	RCSTA

	bsf	STATUS,RP0
	movlw	0x24		; enable 8-bit transmitter in async mode
				; (x16 baud divisor)
	movwf	TXSTA ^ 0x80

;; Configure timer0 - generate periodic interrupt at ~400 times powerline freq

	movlw	0x08		; assign prescaler to WDT (i.e. not used)
	movwf	OPTION_REG ^ 0x80; 
	bcf	STATUS,RP0
	
;; Set up timer1 - simple 16-bit counter, used to count interval between zero-
;; crossings.
	movlw	1
	movwf	T1CON

;;
;; Set up timer2 and ccp module.  These are used to generate a 2.5 MHz clock on
;; the ccp output pin, which is used for clocking data into the 595 chips.  The
;; 
	clrf	T2CON
	bsf	STATUS,RP0
	
	movlw	1	
	movwf	PR2 ^ 0x80	; set timer2 to divide-by-two...
	bcf	STATUS,RP0

	movlw	1
	movwf	CCPR1L		; set up PWM register for minimum duty cycle
	movlw	0x0C		; 
	movwf	CCP1CON		; set CCP module to PWM mode

;; Next two wait loops ensure that we are on the rising edge of ZC when we
;; first enter the mainloop, so that we don't put an overly small value in the
;; timer0_preset register.
	
wait_nzc:
	btfsc	ZC
	goto	wait_nzc
wait_zc:
	btfss	ZC
	goto	wait_zc

	movlw	(1 <<GIE)
	movwf	INTCON		; enable interrupts

mainloop_clear_zc:	
	clrf	zc_delayline

mainloop:
	btfsc	PIR1,RCIF
	call	process_rx

	btfss	ZC
	goto	mainloop_clear_zc
;;
;; zero crossing signal detected
;;
	movlw	0x20		; wait until we see ZC eight times in a row
	addwf	zc_delayline,f
	btfss	STATUS,C
	goto	mainloop
;;
;; zero crossing signal detected 8 times in a row (i.e. noise filtered out)
;;
	bcf	INTCON,T0IE	; turn off timer interrupts
	
;; auto line-frequency detection - measure number of instructions using TMR1
;; while ZC has been low, divide by 256, and multiple by 4/3.  TMR1H should be
;; around 162 for 60 HZ (5,000,000 / 120 / 256) or around 195 (for 50 Hz), and
;; should end up around 215 (for 60 HZ) or around 256 (for 50 Hz) after it has
;; been multiplied by 4/3 (4/3 = 256 / 192, because this firmware has 192
;; possible dimmer levels).
	
;;	bsf	TEST_STROBE
	movf	TMR1H,w
;;	movlw	0xC0
	movwf	acc		; now multiple by 4/3.
	addlw	0		; clear carry
	rrf	acc,f
	addlw	0
	rrf	acc,f
	addwf	acc,f		; * 1 + 1/4 (* 1.25)
	swapf	acc,w
	andlw	0x0F		; * (1.25 / 16) = *.078125

	btfss	STATUS,C	; do addition if previous addition didn't carry
	addwf	acc,f		; * 1.33 (approx).
	btfsc	STATUS,C	; make sure we haven't overflowed
	goto	skip_preset_update

	comf	acc,w
	addlw	150		; check to make sure acc + len(ISR) < 256
	btfsc	STATUS,C
	goto	skip_preset_update
	addlw	109		; restore w, compensate for timer quirks.
	movwf	timer0_preset
skip_preset_update:	
;;	bcf	TEST_STROBE
	call	do_zc_part1
	btfsc	PIR1,RCIF
	call	process_rx
	call	do_zc_part2

;;
;; loop here while zc is still detected...process uart rx events.
;;
	
mainloop1:	
	btfsc	PIR1,RCIF
	call	process_rx
	btfsc	ZC
	goto	mainloop1
				; falling edge of ZC - reset/restart the timers
	movlw	0xF8
	movwf	TMR0		; should generate interrupt fairly quickly.
	clrf	TMR1L
	clrf	TMR1H
	
	bcf	INTCON,T0IF
	bsf	INTCON,T0IE
	goto	mainloop

;;
;; Process an incoming character, using the 'old' Renard protocol
;;
process_rx:
	bcf	STATUS,RP0

	btfsc	RCSTA,OERR	; overrun error
	goto	rx_overrun
	btfsc	RCSTA,FERR	; framing error ... must be read before RCREG
	goto	rx_frame_err

	movf    RCREG,w
	
	addlw	0x82		; test for sync character
	btfsc	STATUS,Z	; skip if sync character
	goto	rx_sync		; jump to code for processing non-sync

;; 
;; process non-sync character
;;
;; rx_state:
;; xxxxx100 -> discarding data (at startup, and after an rx error)
;; xxxxx110 -> echoing data (count was > 0 or after end of local data)
;; xxxxx101 -> expecting command/address byte (just after SYNC)
;; xxxxx001 -> process character after an ESC
;; xxxxx000 -> process short-form data character

	addlw	1		; test for PAD character - discard without echo
	btfsc	STATUS,Z
	return			; silently discard the PAD character (0x7D)

	addlw	0xFE		; test for ESC...Z flag will be set if yes.

	btfsc	STATUS,Z
	goto	rx_ESC		; jump if current character is ESC

	addlw	0x7F		; restore character

	btfsc	rx_state,2
	goto	special_states

	btfsc	rx_state,0	; check if previous character was an ESC
	addlw	0x4E		; adjust value if it was.

	movwf	INDF		; store the new dimmer level

;; recalculate dimmer level (convert range from [1,255] to [64,255]).

	btfsc	STATUS,Z
	goto	skip_rescale	; if level is 0, skip the range conversion
	
	rrf	INDF,f
	bsf	INDF,7		; INDF = (rx >> 1) | 0x80 (/2 + 128)
	addwf	INDF,f
	rrf	INDF,f
skip_rescale:
	
	bsf	rxd_countdown,6
		
;; increment pointers/state (but don't echo the character, since it's for us)

	clrf	rx_state	; 

	incf	FSR,f		; destination pointer
	btfss	FSR,6
	return			; definitely not finished 
	btfss	FSR,5		; finished storing 64 dimmer levels in memory?
	return
	movlw	0x06		; 
	movwf	rx_state	; if yes, enter the 'echo' rx_state
	return			; worst case is 33 instructions.

rx_ESC:	
	bsf	rx_state,0
	return

;;
;; Discarding, echoing, or handling command/acddress byte.
;;
;; The rx character is in w (exactly as it was received).
;;
special_states:

	btfss	rx_state,0	; skip if first character after SYNC
	goto	echo_discard
		
decode_cmd_byte:		; first character after the SYNC
	incf	rx_state,f	; in case packet not for us. (0x05 -> 0x06)

	movwf	FSR		; check original bit 7 to see if proper format
	btfss	FSR,7
	goto	rx_echo		; no, echo the char (stay in echo state)

;; MSB of W is known to be set at this point

	addlw	0x78		; these two commands subtract 8, and set CY if
	iorlw	0x80		; part of packet is for us.
	btfsc	STATUS,C	; 
	goto	rx_echo		; jump and echo decremented value.
	
	clrf	rx_state	; next state is normal body

	swapf	FSR,f		; these two instructions rotate left by 3 bits
	rrf	FSR,w
	andlw	0x38		; mask extraneous bits.
	
	addlw	slot_id_ckt0	; location to store decoded bytes
	movwf	FSR
	
	movlw	0x80
	movwf	TXREG
	return			; 40 instructions to here.

;;
;; Handle echo or discard cases ... no change of state.
;;
echo_discard:
	btfsc	rx_state,1
	movwf	TXREG
	return
;; 
;; process sync character (0x7E)
;;
rx_sync:
	movlw	0x05		; next char s/b command/address byte
	movwf	rx_state
	movlw	0x7E		; re-form the sync char for echoing.

rx_echo:	
	movwf	TXREG
	return
	
rx_frame_err:
	movlw	128
	movwf	frame_err_countdown
	bsf	porta_image,FRAME_ERR_BIT
	goto	rx_error
rx_overrun:
	movlw	128
	movwf	overrun_countdown
	bsf	porta_image,OVERRUN_BIT	
rx_error:			; uart rx error - either framing or overrun
	movf    RCREG,w

	bcf	RCSTA,CREN	; clear the error condition in the uart
	bsf	RCSTA,CREN
	movlw	0x4		; enter discard state
	movwf	rx_state
	return

do_zc_part1:		
	movf	FSR,w		; exchange FSR,_fsr
	xorwf	_fsr,w		; xor of FSR,_fsr
	xorwf	_fsr,f
	xorwf	FSR,f

	movlw	64 + NULLED_CELLS
	movwf	isr_counter
	
	bcf	porta_image,HEARTBEAT_BIT
	incf	heartbeat,f
	btfsc	heartbeat,7
	bsf	porta_image,HEARTBEAT_BIT

	bsf	porta_image,RXD_BIT; assume the LED is off
	decfsz	rxd_countdown,f	; decrement the live time
	bcf	porta_image,RXD_BIT; and turn the LED on if it hasn't expired
	btfsc	porta_image,RXD_BIT
	incf	rxd_countdown,f

	decfsz	frame_err_countdown,f
	goto	skip1
	bcf	porta_image,FRAME_ERR_BIT
skip1:

	decfsz	overrun_countdown,f
	goto	skip2
	bcf	porta_image,OVERRUN_BIT
skip2:
	
	bsf	END_PWM
	movf	porta_image,w
	movwf	PORTA		;
		
	bsf	T2CON,TMR2ON	; turn on clock to 595, to clear it out.
	movlw	slot_id_ckt0
	movwf	FSR

	movf	INDF,w
	movwf	ctr_ckt0
	incf	FSR,f

	movf	INDF,w
	movwf	ctr_ckt1
	incf	FSR,f

	movf	INDF,w
	movwf	ctr_ckt2
	incf	FSR,f

	movf	INDF,w
	movwf	ctr_ckt3
	incf	FSR,f

	movf	INDF,w
	movwf	ctr_ckt4
	incf	FSR,f

	movf	INDF,w
	movwf	ctr_ckt5
	incf	FSR,f

	movf	INDF,w
	movwf	ctr_ckt6
	incf	FSR,f

	movf	INDF,w
	movwf	ctr_ckt7
	incf	FSR,f

	movf	INDF,w
	movwf	ctr_ckt8
	incf	FSR,f

	movf	INDF,w
	movwf	ctr_ckt9
	incf	FSR,f

	movf	INDF,w
	movwf	ctr_cktA
	incf	FSR,f

	movf	INDF,w
	movwf	ctr_cktB
	incf	FSR,f

	movf	INDF,w
	movwf	ctr_cktC
	incf	FSR,f

	movf	INDF,w
	movwf	ctr_cktD
	incf	FSR,f

	movf	INDF,w
	movwf	ctr_cktE
	incf	FSR,f

	movf	INDF,w
	movwf	ctr_cktF
	incf	FSR,f

	movf	INDF,w
	movwf	ctr_ckt10
	incf	FSR,f

	movf	INDF,w
	movwf	ctr_ckt11
	incf	FSR,f

	movf	INDF,w
	movwf	ctr_ckt12
	incf	FSR,f

	movf	INDF,w
	movwf	ctr_ckt13
	incf	FSR,f

	movf	INDF,w
	movwf	ctr_ckt14
	incf	FSR,f

	movf	INDF,w
	movwf	ctr_ckt15
	incf	FSR,f

	movf	INDF,w
	movwf	ctr_ckt16
	incf	FSR,f

	movf	INDF,w
	movwf	ctr_ckt17
	incf	FSR,f

	movf	INDF,w
	movwf	ctr_ckt18
	incf	FSR,f

	movf	INDF,w
	movwf	ctr_ckt19
	incf	FSR,f

	movf	INDF,w
	movwf	ctr_ckt1A
	incf	FSR,f

	movf	INDF,w
	movwf	ctr_ckt1B
	incf	FSR,f

	movf	INDF,w
	movwf	ctr_ckt1C
	incf	FSR,f

	movf	INDF,w
	movwf	ctr_ckt1D
	incf	FSR,f

	movf	INDF,w
	movwf	ctr_ckt1E
	incf	FSR,f

	movf	INDF,w
	movwf	ctr_ckt1F
	incf	FSR,f

	movf	FSR,w		; exchange FSR,_fsr
	xorwf	_fsr,w		; xor of FSR,_fsr
	xorwf	_fsr,f
	xorwf	FSR,f
	
	return

do_zc_part2:	
	movf	FSR,w		; exchange FSR,_fsr
	xorwf	_fsr,w		; xor of FSR,_fsr
	xorwf	_fsr,f
	xorwf	FSR,f
	
	movf	INDF,w
	movwf	ctr_ckt20
	incf	FSR,f

	movf	INDF,w
	movwf	ctr_ckt21
	incf	FSR,f

	movf	INDF,w
	movwf	ctr_ckt22
	incf	FSR,f

	movf	INDF,w
	movwf	ctr_ckt23
	incf	FSR,f

	movf	INDF,w
	movwf	ctr_ckt24
	incf	FSR,f

	movf	INDF,w
	movwf	ctr_ckt25
	incf	FSR,f

	movf	INDF,w
	movwf	ctr_ckt26
	incf	FSR,f

	movf	INDF,w
	movwf	ctr_ckt27
	incf	FSR,f

	movf	INDF,w
	movwf	ctr_ckt28
	incf	FSR,f

	movf	INDF,w
	movwf	ctr_ckt29
	incf	FSR,f

	movf	INDF,w
	movwf	ctr_ckt2A
	incf	FSR,f

	movf	INDF,w
	movwf	ctr_ckt2B
	incf	FSR,f

	movf	INDF,w
	movwf	ctr_ckt2C
	incf	FSR,f

	movf	INDF,w
	movwf	ctr_ckt2D
	incf	FSR,f

	movf	INDF,w
	movwf	ctr_ckt2E
	incf	FSR,f

	movf	INDF,w
	movwf	ctr_ckt2F
	incf	FSR,f

	movf	INDF,w
	movwf	ctr_ckt30
	incf	FSR,f

	movf	INDF,w
	movwf	ctr_ckt31
	incf	FSR,f

	movf	INDF,w
	movwf	ctr_ckt32
	incf	FSR,f

	movf	INDF,w
	movwf	ctr_ckt33
	incf	FSR,f

	movf	INDF,w
	movwf	ctr_ckt34
	incf	FSR,f

	movf	INDF,w
	movwf	ctr_ckt35
	incf	FSR,f

	movf	INDF,w
	movwf	ctr_ckt36
	incf	FSR,f

	movf	INDF,w
	movwf	ctr_ckt37
	incf	FSR,f

	movf	INDF,w
	movwf	ctr_ckt38
	incf	FSR,f

	movf	INDF,w
	movwf	ctr_ckt39
	incf	FSR,f

	movf	INDF,w
	movwf	ctr_ckt3A
	incf	FSR,f

	movf	INDF,w
	movwf	ctr_ckt3B
	incf	FSR,f

	movf	INDF,w
	movwf	ctr_ckt3C
	incf	FSR,f

	movf	INDF,w
	movwf	ctr_ckt3D
	incf	FSR,f

	movf	INDF,w
	movwf	ctr_ckt3E
	incf	FSR,f

	movf	INDF,w
	movwf	ctr_ckt3F
	incf	FSR,f

	movlw	2
	movwf	CCPR1L
	nop			; delay to make sure output of ccp goes high
	nop
	nop
	nop
	bcf	T2CON,TMR2ON	; turn off clock to 595 chip.
	movlw	1
	movwf	CCPR1L
	clrf	TMR2		; timer and ccp prepared for first interrupt!
	bcf	END_PWM
		
	movf	FSR,w		; exchange FSR,_fsr
	xorwf	_fsr,w		; xor of FSR,_fsr
	xorwf	_fsr,f
	xorwf	FSR,f
	
	return
	
  END
