;************************************************************************
; Following the example of Bob Blick's original propeler clock I made 	*
; my own version of this fancy clock and have re-written the code from	*
; scratch. I used a bit more powerfull version of PIC cpu and added 	*
; these features:														*
;  - the clock keeps time AND DATE			           					*
;  - can display analog hands or digital time and mixed mode where		*
;    time is displayed as analog hands and digital date					*
;  - setting the time/date or switch mode is done by RC5 remote control	*
;  - possibility to display a message programmed in ROM or EEPROM		*
;                                                                 		*
;- Extensions made to Soubry Henk's version - in the same PIC cpu: 		*
;  - Shows Day of week,													*
;  - Rotation speed measurement,          					      		*
;  - Dot / arc second hand,												*
;  - Doubble tick at 12,												*
;  - Static text displaying - routine repaired,                    		*
;  - Scrolling message (two on 16F648A) with adjustable speed      		*
;  - Uses PCF8583-5 I2C RTC to store time.                         		*
;                                                                 		*
;************************************************************************
;                                                               		*
;    Filename:	    prop628.asm                                        	*
;    StartDate:     02/12/2001                                        	*
;    LastUpdate:    04/09/2009											*
;    File Version:  2.20                                       	   		*
;                                                                     	*
; Base on the folowing version                                         	*
;    Author:        Soubry Henk                                       	*
;    Company:       Soubry Software Service                           	*
; Rewritten - extended                                                	*
;                                                                     	*
;************************************************************************
;                                                                    	*
;    Files required: CharGen628.asm                                     *
;                    Keys628.asm  	                                    *
;                                                                     	*
;                                                                     	*
;************************************************************************
;                                                                     	*
;    Notes:                                                           	*
;    	Pin assignment                                                	*
;      		Port A                                             			*
;				0 = inner display led (because RB0 is index)			*
;               1 = analog outer led				   					*
;               2 = I2C SCL	use 2k pull up resistor to PIC's Vdd       	*
;               3 = OSC Calibration output @ 2.5kHz                    	*
;				4 = I2C SDA	use 2k pull up resistor to PIC's Vdd		*
;               5 = Ir receiver              Input only pin            	*
;				6..7 = Quartz oscillator								*
;      		Port B 														*
;		0 = index                                     	       			*
;               1..6 = Display leds (1=inner, 6=outer)                	*
;               7 = analog inner leds	  								*
;                                                                     	*
;************************************************************************
;                                                                     	*
; Afther setting time, toggle OuterLed command saves time to RTC		*
;                                                                     	*
;************************************************************************

	ifdef	__16F628
		list	p=16f628              ; 16F628 can be used
		#include <p16f628.inc>        ; processor specific variable definitions
EEPROM_SIZE	EQU	128
    endif
	ifdef	__16F628A
		list	p=16f628A             ; 16F628A can be used
		#include <p16f628A.inc>       ; processor specific variable definitions
EEPROM_SIZE	EQU	128
	endif
	ifdef	__16F648A
		list	p=16f648A             ; 16F648A can be used
		#include <p16f648A.inc>       ; processor specific variable definitions
EEPROM_SIZE	EQU	256
	endif
	ifdef	__16F87
		list	p=16f87				  ; 16F87 can be used
		#include <p16f87.inc>		  ; processor specific variable definitions
		#define	T0IE	TMR0IE
		#define	T0IF	TMR0IF
		#define	_MCLRE_OFF	_MCLR_OFF
EEPROM_SIZE	EQU	256
	endif
	ifdef	__16F88
		list	p=16f88				  ; 16F88 can be used
		#include <p16f88.inc>		  ; processor specific variable definitions
		#define	T0IE	TMR0IE
		#define	T0IF	TMR0IF
		#define	_MCLRE_OFF	_MCLR_OFF
EEPROM_SIZE	EQU	256
	endif

	ifndef	EEPROM_SIZE
		error	"Invalid processor type selected"
	endif

	#include "CharGen628.asm"		; Chacacter dot table
	#include "Keys628.asm"			; Remote controller address and command code definitions

	ifdef	__16F88
		__CONFIG _CONFIG1, _CP_OFF & _WDT_OFF & _BODEN_ON & _PWRTE_ON & _HS_OSC & _MCLRE_OFF & _LVP_OFF
		__CONFIG _CONFIG2, _IESO_OFF & _FCMEN_OFF 
	else
	  ifdef	__16F87
		__CONFIG _CONFIG1, _CP_OFF & _WDT_OFF & _BODEN_ON & _PWRTE_ON & _HS_OSC & _MCLRE_OFF & _LVP_OFF
		__CONFIG _CONFIG2, _IESO_OFF & _FCMEN_OFF 
	  else
		__CONFIG _CP_OFF & _WDT_OFF & _BODEN_ON & _PWRTE_ON & _HS_OSC & _MCLRE_OFF & _LVP_OFF
	  endif
	endif

	__IDLOCS 0xA220

;***** CONDITION DEFINITIONS

;#define RC5IntOnRA5				; un-comment this line if RC5 input is on RA2
#define	MotorCounterClockWise	; un-comment this line if motor is running counter clockwise
;#define	ReversTextScroll		; un-comment this line if your text must scroll from left to right (e.g. Hebrew)
#define DateFormat_YYMMDD		; un-comment this lime if you prefere this format default id DDMMYY
;#define RC5AddrCheck			; comment this line if RC5 Address checking not required
;#define UsePCF8583RTC			; comment this line if No PCF8583 RTC
#define DoubleTickAt12			; un-comment this lime if you prefere simple tick at 12
#define	RotationMeas			; un-comment this lime if you prefere not to display rotation measurement
#define	ArcSecMode				; comment this line not to use Acr second hand mode
#define	StaticText				; comment this line not to use static text display
;#define SlashedZero				; comment this line to use normal zero on time/date display

#define	RTC_ADDR		0xA0	; RTC's address with A0 pin grounded
;#define	RTC_Control	0x00	; Control register
#define	RTC_Second		0x02	; Address of Second
;#define	RTC_Minute	0x03	; Address of Minute
;#define	RTC_Hour	0x04	; Address of Hour / AM-PM flag and mode
;#define	RTC_Day		0x05	; Address of Day  / 2 lsb of Year
;#define	RTC_Month	0x06	; Address of Week day / Month
#define RTC_Year        0x10    ; Address of Year
#define RTC_YearNeg     0x11    ; Address of negated value of Year
#define	RTC_flags		0x12	; Address of flags
#define	RTC_disp_off	0x13	; Address of DispOffset

#define DISPLAYOFFSET	0x37	; Initial Display Offset for the hardware (display memory address) !!!

;***** VARIABLE DEFINITIONS

;Display memory
;DispI		EQU     0x20	  ; 40 bytes from 0x20 -> 0x48
;DispII		EQU     0xA0	  ; 80 bytes from 0xA0 -> 0xF0

;Vars in shared memory. This memory is available from any bank
  cblock	0x070
	Scratch			; memory locations for general use
	Scratch2		;
	Scratch3		;
	Scratch4		;

;Rotation Measurement
	Rotation_L		; Rotation measurement
	Rotation_H

;Vars for building display-content - These variables used for I2C communication as well
	BCD				; Binary coded decimal					; Output shift register
	digitindex		; index digit to display				; Word address of data in RTC
	dotindex		; index dot to display					; Temporary storage
	dpi				; 0..119 display index for main program	; Delay loop counter
	tdd				; temporary display data				; Data to be writtem to RTC, input shift register
;					;										; FSR is the bit counter

; Context saving variables
	w_temp			; for w
	status_temp		; for STATUS
	fsr_temp		; for FSR

; General flags
	flags			; Display flags
	flags2			; System  flags
  endc

	IF flags2 > 0x7F
		ERROR "To many variables used in Common RAM"
	ENDIF

;Vars for display timing routines and interrupts
  cblock	0x020
	DispI:.40		; Display memory first part

	PeriodCnt_H		; 16 bit counter counts number of interrupt
	PeriodCnt_L		; between index-puls (one revolution)

  ;!! Keep variable order - indirect access used to them v
	StorePCnt1_H	; first  16 bit storage for PeriodeCounter
	StorePCnt1_L	;
	StorePCnt2_H	; second 16 bit storage for PeriodeCounter
	StorePCnt2_L	;
  ;!! Keep variable order - indirect access used to them ^

	SubSec_H		; 16 bit sub-second counter
	SubSec_L		; there are 2500 sub-seconds in 1/2 second

	PixelWidth		; Pixel-width = number of interrupts in one ON pixel
	PixelOff		; 8 bit down-counter to time PixelWidth

	PixelPitch_H	; PixelPitch = 16 bit number of interrupts between two pixel
	PixelPitch_L	;
	PixelPitch_F	; Fraction of PixelPitch needed for accuracy and to avoid jitter

	NewPPitch_H		; New pixel pitch calculated by main programm, will be
	NewPPitch_L		; copied to PixelPithc by interrupt routine
	NewPPitch_F		;

	NextPixel_H		; Next pixel @ PeriodCnt = NextPixel
	NextPixel_L		;
	NextPixel_F		;
	RoundedNext_L	; RoundedNext = Round(NextPixel)
	RoundedNext_H	;

	iFSR			; Display memory pointer of interrupt routine
	DispOffset		; Display offset compared to indexpuls

	TmrScroll		; Count Down timer for scroll delay

;Vars for time-keeping
  ;!! Keep variable order - Timecheck use indirect access to them v
	Second2			; 0..119 counting half seconds
	Minute			; 0.. 59
	Hour			; 0.. 23
	Day				; 1.. Dmon-1
	Month			; 1.. 12
	Year			; 0.. 99
  ;!! Keep variable order - Timecheck use indirect access to them ^

	DMon			; days in current Month + 1
	WDay			; Day of week 0..6 - displayed as 1..7 (language independent)
					; Sunday can be 1 or 7 as You like it

;Rotation BCD data
	RotBCD			;   1 r/m
	RotBCD100		; 100 r/m

;Vars for RC5 decoding
	RC5_flags		; Receivers state flags
	RC5_Tmr			; Receiver's time counter
	RC5_BitCnt		; Receiver's bit counter
	RC5_Addr		; New command address just received - only lower 5 bits are valid
	RC5_Cmd			; New command code just received - Bit 7 is the toggle bit
	RC5_Cmd2		; storage for previous cmd
  endc

	IF RC5_Cmd2 >= 0x70
		ERROR "To many variables used in Bank0"
	ENDIF

  cblock	0x0A0
					; More RAM space avaible: 80
	DispII:.80		; Display memory second part
	; Not to use memory 0x0F0..0x0FF  -  it will overwrite 0x070..0x07F
  endc

	IF DispII+.80 > 0xF0
		ERROR "To many variables used in Bank1"
	ENDIF

  cblock	0x120
					; More RAM space avaible: 48 for 16F628(A) and 80 for 16F684A ,16F87, 16F88
	ch_dot_point_H	; pointer the the current dot pattern of current character
	ch_dot_point_L	; 16 bit offset to the chargenerator table
	ch_dot_index	; index of dot pattern in a char, 0..5
	ch_blanking		; counter for blanking the display when scrolling text

 	; Not to use memory 0x170..0x17F  -  it will overwrite 0x070..0x07F
 endc

	IF ch_blanking >= 0x170
		ERROR "To many variables used in Bank2"
	ENDIF

	; On Bank3 only the common memory avaible on these processors
	; Not to use memory 0x1F0..0x1FF  -  it will overwrite 0x070..0x07F

;**********************************************************************
; give meaningfull names to the scratch locations

; for the 16bit/8bit divide routine - Can be used from any Bank
DIV_HI		EQU	Scratch	; 16 bit Divident
DIV_LO		EQU	Scratch2; needed for 16 bit / 8 bit division
DIV_Q		EQU	Scratch3; Quotient
DIVISOR		EQU	Scratch4; Divisor

;**********************************************************************
; define Port bits
;PortA
bPixel0		EQU	0
bOuterLED	EQU	1
bCalibration	EQU	3

  ifdef	UsePCF8583RTC
bSCL		EQU	2
bSDA		EQU	4
  endif

  ifdef	RC5IntOnRA5
bRC5inp		EQU	5
  else
bRC5inp		EQU	2
  endif

  ifdef UsePCF8583RTC
	if ((bOuterLED>5) || (bCalibration>5) || (bSCL>5) || (bSDA>5) || (bRC5inp>5))
	  error "RA6, RA7 pins are used for quartz oscillator"
	endif
  else
	if ((bOuterLED>5) || (bCalibration>5) || (bRC5inp>5))
	  error "RA6, RA7 pins are used for quartz oscillator"
	endif
  endif

  ifdef UsePCF8583RTC
	if ((bOuterLED==5) || (bCalibration==5) || (bSCL==5) || (bSDA==5))
	  error "RA5 in an input only pin"
	endif
  else
	if ((bOuterLED==5) || (bCalibration==5))
	  error "RA5 in an input only pin"
	endif
  endif

	if ((bOuterLED==4) || (bCalibration==4))
	  messg "RA4 in an open drain output pin - pull-up resistor required"
	endif

	ifdef	UsePCF8583RTC
		if ((bOuterLED==bSCL) || (bCalibration==bSCL) || (bPixel0==bSCL) || (bSDA==bSCL) || (bRC5inp==bSCL) || (bSCL>4))
			error "PORTA configuration error : bSCL"
		endif
		if ((bOuterLED==bSDA) || (bCalibration==bSDA) || (bPixel0==bSDA) || (bSDA==bSCL) || (bRC5inp==bSDA) || (bSDA>4))
			error "PORTA configuration error : bSDA"
		endif
	endif

;PortB
bIndexPuls	EQU	0		; Index pulse interrupt request
bSecLED		EQU 5		; Second hand's LED
bTickLED	EQU	6		; Analogue clock's Ticks LED
bInnerLED	EQU	7		; Analogue hand's inner 9 LEDs

	if (bIndexPuls!=0)
	  error "Index puls has to be the RB0/External interrupt pin"
	endif

	if ( (bIndexPuls==bSecLED)   || (bTickLED==bSecLED)  || (bInnerLED==bSecLED) )
	  error "PORTB configuration error : bSecLED"
	endif

	if ( (bIndexPuls==bTickLED)  || (bSecLED==bTickLED)  || (bInnerLED==bTickLED) )
	  error "PORTB configuration error : bTickLED"
	endif

	if ( (bIndexPuls==bInnerLED) || (bSecLED==bInnerLED) || (bTickLED==bInnerLED) )
	  error "PORTB configuration error : bInnerLED"
	endif

; TMR1L, TMR1H	used to count rotation speed

; T1CON			used to store flags - Bits are cleared at reset
bRC5_TimeSet EQU 2		; Time setting commands were received
bNewRot		EQU	4		; New rotation count avaible
bPStorage	EQU	5		; Rotation time measurement buffer select

; CCPR1L	used for copy of PORTA
; PORTA read-modify-write instructions set SCL and SDA to 1 -- external pull up resistor
; PORTA read-modify-writes disturb I2C routines - CCPR1L is used to store value written to PORTA

; CCPR1H	used to store Scrolling speed

; flag bits				; Display flags / read form RTC
bShowWDay	EQU	0
bShowOuter	EQU	1
bShowRot	EQU	2
bSecMode	EQU	3
bShowTicks	EQU	4
bShowHand	EQU	5
bShowDTime	EQU	6
bShowDDate	EQU	7

; flag2 bits			; System  flags / Cleared at reset
bDspEnabled	EQU	0		; Blank the display - motor slow or not stabilized
bNewPCnt	EQU	1
bNewTime	EQU	2
bNewPPitch	EQU	3
bText		EQU	4
bScrollOn	EQU	5
bDemo		EQU	6
bTimeInv	EQU 7

; RC5 flags					; Cleared in interrupt routine at RC5_error
bRC5_WaitStart	EQU	0
bRC5_DataReady	EQU	1
;				EQU	2		; Reserved for backward compatibility
bRC5_Idle		EQU	3
bRC5_ReSynced	EQU	4
bRC5_prev_inp	EQU	bRC5inp	; Has to be on same bit as bRC5inp on PortA
bRC5_HalfBit	EQU	6
;				EQU	7		; Can be used, but interrupt routine clears RC5_flags if error in communication

	if ((bRC5_WaitStart==bRC5_prev_inp) || (bRC5_DataReady==bRC5_prev_inp) || (bRC5_HalfBit==bRC5_prev_inp) || (bRC5_Idle==bRC5_prev_inp) || (bRC5_ReSynced==bRC5_prev_inp))
	  error "RC5_flags configuration error"
	endif

  ifdef UsePCF8583RTC
	if ((bPixel0==bRC5_prev_inp) || (bOuterLED==bRC5_prev_inp) || (bSCL==bRC5_prev_inp) || (bCalibration==bRC5_prev_inp) || (bSDA==bRC5_prev_inp) || (bRC5inp!=bRC5_prev_inp))
	  error "PORTA configuration error"
	endif
  else
	if ((bPixel0==bRC5_prev_inp) || (bOuterLED==bRC5_prev_inp) || (bCalibration==bRC5_prev_inp) || (bRC5inp!=bRC5_prev_inp))
	  error "PORTA configuration error"
	endif
  endif

	if (bRC5inp!=bRC5_prev_inp)
	  error "bRC5inp and bRC5_prev_inp have to be on same bit position"
	endif

;**********************************************************************

; PortA  - a copy of PORTA is in CCPR1L
; -!-!- Don't use bsc, bsf, operation with PORTA,f (like xorwf  PORTA,f). Only clrf PORTA and movwf PORTA will work -!-!-
; -!-!- Make modification on CCPR1L and use only XorToPorta, ToPorta rutines -!-!-
; -!-!- Disable interrupt (bcf INTCON,GIE) before these routines, enable (bsf INTCON,GIE) afther -!-!-

;#define		Pixel0		PORTA,BPixel0
#define 	OuterLED	PORTA,bOuterLED
#define		SCL			PORTA,bSCL
#define		Calibration	PORTA,bCalibration
#define		SDA			PORTA,bSDA
#define 	RC5inp		PORTA,bRC5inp

; PortB
#define 	IndexPuls	PORTB,bIndexPuls

; T1CON
#define		RC5_TimeSet	T1CON,bRC5_TimeSet
#define		NewRot		T1CON,bNewRot
#define		PStorage	T1CON,bPStorage

; flags
#define		ShowWDay	flags,bShowWDay
#define 	ShowOuter	flags,bShowOuter
#define		ShowRot		flags,bShowRot
#define		SecMode		flags,bSecMode
#define		ShowHand	flags,bShowHand
#define		ShowDTime	flags,bShowDTime
#define		ShowDDate	flags,bShowDDate
#define		ShowTicks	flags,bShowTicks

; flags2
#define		fDspEnabled	flags2,bDspEnabled
#define		NewPCnt		flags2,bNewPCnt
#define		NewTime		flags2,bNewTime
#define		NewPPitch	flags2,bNewPPitch
#define		fText		flags2,bText
#define		fScrollOn	flags2,bScrollOn
#define		fDemo		flags2,bDemo
#define		fTimeInv	flags2,bTimeInv

; RC5_flags
#define		RC5_WaitStart	RC5_flags,bRC5_WaitStart
#define		RC5_DataReady	RC5_flags,bRC5_DataReady
;2
#define		RC5_Idle		RC5_flags,bRC5_Idle
#define		RC5_ReSynced	RC5_flags,bRC5_ReSynced
#define		RC5_prev_inp	RC5_flags,bRC5_prev_inp
#define		RC5_HalfBit		RC5_flags,bRC5_HalfBit
;7

;******************************************************************************
;	Define macro's for a Select - Case
;******************************************************************************
select_w	macro
sel_last	set 0                     ;setup variable only
         	endm
case     	macro   val
	        xorlw   val ^ sel_last
		btfsc	STATUS,Z
sel_last set val
         	endm

;******************************************************************************
;	Define EEPROM content for messages - No leadout needed
;******************************************************************************

	if	EEPROM_SIZE==256
		ORG		0x2180			; Start off message2 in EEPROM
		de	"===> Displays day of week, rotation speed and store times in PCF8583 RTC ",0x83,0x84," <==="
		de	0x00
	endif

		ORG		0x2170			; Static text
		de	"Propeller clock", 0x00

		ORG     0x2100			; Start off message1 in EEPROM
		de	"===> This version of Bob Blick's Propeller Clock was build and programmed by Henk Soubry ",0x83,0x84
    ifdef	__16F628
		de	" 16F628"
	endif
    ifdef	__16F628A
		de	" 16F628A"
	endif
	ifdef	__16F648A
		de	" 16F648A"
	endif
	ifdef	__16F88
		de	" 16F88"
	endif
	ifdef	__16F87
		de	" 16F87"
	endif
		de	" V2.20 <==="
		de	0x00

;**********************************************************************
		ORG     0x000           ; processor reset vector
		clrf	PORTA			; all LED's OFF
		clrf	PORTB
		clrf	CCPR1L			; Clear copy of PORTA
		goto    main            ; go to beginning of program

;**********************************************************************
		ORG     0x004           ; interrupt vector location
		movwf	w_temp			; Save W
		swapf	w_temp, f		; Prepare to restore with another swapf
		swapf	STATUS, w		; Get status
		movwf	status_temp		; Save it
		movf	FSR,w			; Get FSR
		movwf	fsr_temp		; Save it
		clrf	STATUS			; Bank0

;--------
INT_RB0
		btfss	INTCON,INTF		; interrupt on RB0?
		goto	INT_TMR0		; nope, TMR0 is next

;----------	RB0 interrupt ! == Index Sensor
		movlw	0xff			;
		movwf	TMR0			; force TMR0 to 0xFF

		movlw	StorePCnt1_H
		btfss	PStorage
		movlw	StorePCnt2_H	; Get pointer to buffer to use
		movwf	FSR
		movf	PeriodCnt_H,w	; Get counter's high part
		movwf	INDF			; Store it
		incf	FSR,f			; Move pointer
		movf	PeriodCnt_L,w	; Get counter's low  part
		movwf	INDF			; Store it
		movlw	1<<bPStorage	; Switch to other buffer
		xorwf	T1CON,f

StoreDone
		bsf		NewPCnt			; signal new stored PeriodCount to main program
		clrf	PeriodCnt_H		; PeriodCnt = 0
		clrf	PeriodCnt_L		;
		clrf	NextPixel_H		; Next_Pixel = 0 (thus now)
		clrf	NextPixel_L		;
		clrf	NextPixel_F		;
		clrf	RoundedNext_L	; RoundedNext = 0
		clrf	RoundedNext_H	;
		clrf	PixelOff		; display next pixel no matter what.
		movf 	DispOffset,w	; iDispIndex = DispOffset
		movwf	iFSR			;
		btfss	NewPPitch		; is there a new value calculated by main prg?
		goto	lINT_RB0		; no, continue
		movf	NewPPitch_H,w	; PixelPitch = NewPPitch
		movwf	PixelPitch_H	; High part
		movf	NewPPitch_L,w	;
		movwf	PixelPitch_L	; Low  part
		movf	NewPPitch_F,w	;
		movwf	PixelPitch_F	; Fraction part
		bcf		NewPPitch		; Clear new value calculated flag

lINT_RB0
	ifdef	RotationMeas
		incfsz	TMR1L,f			; Increment rotation counter low  part
		goto	INT_RB0_2		; Skip if not zero
		incf	TMR1H,f			; Increment rotation counter high part
INT_RB0_2
	endif

		bcf		INTCON,INTF		; clear RB0 interrupt flag
		goto	lDisp_1			; do display routine but skip Period increment
;--------
INT_TMR0
		btfss	INTCON,T0IF		; Test if a TMR0 interrupt occured
		goto	INT_TMR2		; nope, TMR2 is next

		bsf		TMR0,7			; TMR0 = TMR0 + 128 => Forse to double TMR0 speed

		incfsz	PeriodCnt_L,f	; Increment 16-bit period counter
		goto	lDisp_1			;
		incfsz	PeriodCnt_H,f	;
		goto	lDisp_1			; if overflow in MSB period counter = speed to low

		bcf		flags2,bDspEnabled
	ifdef	UsePCF8583RTC
		bcf		CCPR1L,bOuterLED; Other bit of PortA, PortB bits will be cleared by lDisp_1
		call	ToPorta
	else
		bcf		PORTA,bOuterLED	; Other bit of PortA, PortB bits will be cleared by lDisp_1
	endif
lDisp_1
		movf	RoundedNext_L,w	; PeriodCnt = RoundedNextPixel ?
		xorwf	PeriodCnt_L,w	;
		btfss	STATUS,Z		;
		goto	lPixelOn		; no, check if there is a pixel on
		movf	RoundedNext_H,w	;
		xorwf	PeriodCnt_H,w	;
		btfss	STATUS,Z		;
		goto	lPixelOn		; no,  check if there is a pixel on
								; yes, display next pixeldata

		movf	iFSR,w			; load new memory pointer, i(nterrupt)FSR
		movwf	FSR				; load File Select register with Display Index
		movlw	0x00			; Blank the display if rotation too slow
		btfsc	flags2,bDspEnabled
		movf	INDF,w			; PortB = DisplayData(index)
		movwf	PORTB			;
	ifdef	UsePCF8583RTC
		xorwf	CCPR1L,w		; dispatch bit 0 to PORTA
		andlw	1<<bPixel0
		call 	XorToPorta		; only bit0 will change
	else
		xorwf	PORTA,w			; dispatch bit 0 to PORTA
		andlw	1<<bPixel0
		xorwf	PORTA,f			; only bit0 will change
	endif

	ifdef MotorCounterClockWise
		call	CheckDecrementFSR; decrement FSR, check correct progress of diplay memory pointer
	else
		call	CheckIncrementFSR; increment FSR, check correct progress of diplay memory pointer
	endif
		movwf	iFSR			; Store new value to i(nterrupt)FSR

		movf	PixelPitch_F,w 	; NextPixel = NextPixel + PixelPitch
		addwf	NextPixel_F,f	;
		btfss	STATUS,C		;
		goto	no_overflow		;
		incf	NextPixel_L,f	; fraction overflow, add 1 to Low byte
		btfsc	STATUS,Z		;
		incf	NextPixel_H,f	; low byte roll-over, add 1 to High byte
no_overflow:
		movf	PixelPitch_L,w	;
		addwf	NextPixel_L,f	;
		btfsc	STATUS,C		;
		incf	NextPixel_H,f	; low byte overflow, add 1 to High byte

		movf	NextPixel_L,w	; RoundedNext = NextPixel
		movwf	RoundedNext_L	;
		movf	NextPixel_H,w	;
		movwf	RoundedNext_H	;
		btfss	NextPixel_F,7	; IF NextPixel_Fraction >= 128 then
		goto	NotRoundUp		;
		incf	RoundedNext_L,f	; ROUND UP
		btfsc	STATUS,Z		;
		incf	RoundedNext_H,f	;
NotRoundUp:
		movf	PixelWidth,w	;
		movwf	PixelOff		; PixelOff = PixelWidth

		goto	lDispDone		;

lPixelOn
		movf	PixelOff,w		; Is there a pixel on?
		btfsc	STATUS,Z		; PixelOff = 0 ?
		goto	lDispDone		; no, jump

		decfsz	PixelOff,f		; pixel is on, countdown
		goto	lDispDone		;

		clrf	PORTB			; turn off display LEDs an inner LED
	ifdef	UsePCF8583RTC
		bcf		CCPR1L,bPixel0	; also turn off Bit 0 LED
		call	ToPorta
	else
		bcf		PORTA,bPixel0	; also turn off Bit 0 LED
	endif

lDispDone
		bcf		INTCON,T0IF		; clear TMR0 interrupt flag before return

;--------
INT_TMR2
		btfss	PIR1,TMR2IF		; interrupt on TMR2?
		goto	INT_EXIT		; nope, interrupt is done!

								; do the TMR2 stuff, we get here every 200uSec

								; toggle Callibration output
		movlw   1 << bCalibration; use scope or freq counter to calibrate to 2.5 kHz
	ifdef	UsePCF8583RTC
		call	XorToPorta
	else
		xorwf	PORTA,f
	endif
								; Text-scroll timer
		movf	TmrScroll,f		; TmrScroll == 0 ?
		btfss	STATUS,Z		;
		decf	TmrScroll,f		; no, TmrScroll --

								; real time sub-second counter
		incfsz	SubSec_L,f		; Increment 16-bit Sub Second counter
		goto	lTime_1			; stil counting
		incfsz	SubSec_H,f		;
		goto	lTime_1			; stil counting

		incf	Second2,f		; 1/2 second has passed
		bsf		NewTime			; signal new time to main program

		call	InitSubSecCnt	; reload counter SubSecond = 0x10000 - .2500 = 0xF63C

	ifdef	UsePCF8583RTC
		movlw	1<<bOuterLED	; Toggle outerled if time invalid
		btfsc	fTimeInv
		call	XorToPorta
	endif

INT_ROT
	ifdef	RotationMeas
		movlw	.120			; Minute passed
		xorwf	Second2,w
		btfss	STATUS,Z
		goto	lTime_1			; No - do RC5 task

		movf	TMR1L,w			; Read rotation from Timer1
		movwf	Rotation_L		; Store it
		movf	TMR1H,w
		movwf	Rotation_H

		clrf	TMR1H			; Reset Timer 1
		clrf	TMR1L

		bsf		NewRot			; Sign new rotation data avaible
	endif

;--------
lTime_1							; start RC5 stuff here
		btfsc	RC5_DataReady	;
		goto	lRC5_Exit		;
		btfss	RC5_Idle		;
		goto	lRC5_Not_Idle	;
		decfsz	RC5_Tmr,f		;
		goto	lRC5_Exit		;
		btfsc	RC5inp			; test input
		bcf		RC5_Idle		; input = high, cancel Idle state
		incf	RC5_Tmr,f		; continue Idle state until input = high
		goto	lRC5_Exit		;

lRC5_Not_Idle
		btfss	RC5_WaitStart	;
		goto	lRC5_ReSync		;

lRC5WaitStart
		btfsc	RC5inp			; test input
		goto	lRC5_Exit		; no startbit
		bcf		RC5_WaitStart	; start received
		movlw	.13				; 13 bits to receive
		movwf	RC5_BitCnt		;
		clrf 	RC5_Addr		; Clear address and command
		clrf	RC5_Cmd			;
		movlw	.6				;
		goto	lRC5_Reload

lRC5_ReSync						;
		movf	PORTA,w			; Read input
		xorwf	RC5_flags,w		; Test if it is the same as in RC5_Flags
		andlw	1<<bRC5inp		; Only RC5 input bit is tested
		btfsc	STATUS,Z		;
		goto	lRC5_no_sync	;
		bsf		RC5_ReSynced	;

		xorwf	RC5_flags,f		; Store new bit in RC5_flags

		movlw	.6				; re-sync the timer
		btfss	RC5_HalfBit		;
		movlw	.2				;
		movwf	RC5_Tmr			;

lRC5_no_sync
		btfsc	RC5_HalfBit		;
		goto	lRC5_2nd_Half	;

lRC5_1st_Half
		decfsz	RC5_Tmr,f		; Decrement RC5 timer
		goto	lRC5_Exit		; If timer != 0 -> Exit
		bcf		STATUS,C		;
		btfsc	RC5inp			;
		bsf		STATUS,C		; C = RC5inp
		rlf		RC5_Cmd,f		;
		rlf		RC5_Addr,f		;
		bsf		RC5_HalfBit		; indicate that the first half bit is received
		bcf		RC5_ReSynced	;
		movlw	.4				;
		goto	lRC5_Reload

lRC5_2nd_Half
		btfsc	RC5_ReSynced	;
		goto	lReSyncOK		;
		decfsz	RC5_Tmr,f		;
		goto	lRC5_Exit		;
lRC5_Error
		movlw	(1<<bRC5_WaitStart) | (1<<bRC5_Idle)
		movwf	RC5_flags		; Reinit RC5 flags and timer
		movlw	.128			;
lRC5_Reload
		movwf	RC5_Tmr			;
		goto	lRC5_Exit		;

lReSyncOK
		bcf		RC5_HalfBit		; test second bit half
		decfsz	RC5_BitCnt,f	;
		goto	lRC5_Exit		;
		rlf		RC5_Cmd,f		; Shift left to complete Addr
		rlf		RC5_Addr,f		;
		rlf		RC5_Cmd,w		; RC5_Cmd remains unchanged
		rlf		RC5_Addr,f		; Complete address
		bcf		RC5_Cmd,7		;
		btfss	RC5_Addr,6		;
		bsf		RC5_Cmd,7		; Bit 7 of RC5_Cmd = Inv Bit6 in RC5_Addr
								; RC5_Addr and RC5_Cmd was cleared and shifted left 13+2 times, so C = 0
		btfsc	RC5_Addr,5		;
		bsf		STATUS,C		; C = ToggleBit
		rrf		RC5_Cmd,f		; ToggleBit in bit 7 of RC5_Cmd
		bsf		RC5_DataReady	; Only lower 5 bits of RC5_Addr are valid
lRC5_Exit
		bcf		PIR1,TMR2IF		; clear TMR2 interrupt flag
;--------
INT_EXIT						; context recall
		movf	fsr_temp,w		; Get saved FSR
		movwf	FSR				; Restore it
		swapf	status_temp, w	; Get saved STATUS
		movwf	STATUS			; Restore it, this will also restore bank selection
		swapf	w_temp, w		; Restore saved W
		retfie                  ; return from interrupt

	ifdef	UsePCF8583RTC
;**********************************************************************
; Potra write routines for I2C compatibility
; Use only these routines to copy CCPR1L to PORTA
; disable interrupts if called other routines not from interrupt routine

XorToPorta
		xorwf	CCPR1L,f
ToPorta
		movf	CCPR1L,w
		movwf	PORTA
		return
	endif

;******************************************************************************
;	Init sub second counter	--	SubSecond = 0x10000 - 2500 = 0xF63C

InitSubSecCnt
		movlw	0x3C			; load counter SubSecond = 0x10000 - 2500 = 0xF63C
		movwf	SubSec_L		;
		movlw	0xF6			;
		movwf	SubSec_H		;
		retlw	0x07			; Return data to disable comparator

;******************************************************************************
; Convert binary number to BCD

Conv2BCD						; Convert w to BCD (0 <= w <= 99)
		clrf	Scratch2		; Clear high nibble
l_bcd_1
		addlw	-.10			; Sub .10
		incf	Scratch2,f		; Inc high nibble
		btfsc	STATUS,C		; Loop if no carry
		goto	l_bcd_1
		addlw	.10				; Re add .10, C=1
		decf	Scratch2,f		; Correct high nibble
		swapf	Scratch2,f		; Swap to high part of byte
		addwf	Scratch2,w		; Add to low nibble, C=0
		return

;**********************************************************************
; 16 bit by 8 bit division to 8 bit quotient and 8 bit remainder

div16X8Divisor					; Store divisor
		movwf	DIVISOR

div16X8							; DIV_HI and DIV_LO / DIVISOR.  result to DIV_Q
								; remainder in DIV_LO
								; does not deal with divide by 0 case
		clrf 	DIV_Q
div_1
		movf 	DIVISOR, W
		subwf 	DIV_LO, F
		btfsc 	STATUS, C		; if negative skip
		goto 	div_2
div_borrow
		movlw 	.1
		subwf 	DIV_HI, F		; DIV_HI = DIV_HI - 1
		btfss 	STATUS, C		; if no borrow occurred
		goto	div_done
div_2
		incf 	DIV_Q, F
		goto 	div_1
div_done
		movf 	DIVISOR, W		; re-add DIVISOR to DIV_LO to get
		addwf 	DIV_LO, W		; remainder in DIV_LO
		movwf	DIV_HI			; shift remainder to DIV_HI
		movf	DIV_Q,w			; Return with quotient in w

		return

;**********************************************************************
; Calculate pixel pitch

CalcPixelPitch					; that is a hard one. We have to divide by 120 !!!
								; PixelPitch = PeriodCnt / 120

		movf 	StorePCnt1_H,w	; check if two period counters
		movwf	DIV_LO			;
		xorwf	StorePCnt2_H,w	; are the same. If not, don't
		btfss	STATUS,Z		; calculate new pixelpitch because
		goto	CalcDone		; the rotation speed was not stable
		movf	StorePCnt1_L,w	;
		xorwf	StorePCnt2_L,w	;
		btfss	STATUS,Z		;
		goto	CalcDone		;

		clrf	DIV_HI			; Divider = 00:StorePCnt1_H

		movlw	.120			; Init divisor
		call 	div16X8Divisor
		movwf	NewPPitch_H		;
		movf	StorePCnt1_L,w	;
		movwf	DIV_LO			;

		call	div16X8			; Divider = Remainder:StorePCnt1_L
		movwf	NewPPitch_L		;
		clrf	DIV_LO			;

		call	div16X8			; Divider = Remainder:00
		movwf	NewPPitch_F		;

								; start calculation for pixel width
		clrc					; Clear Carry
		rrf		NewPPitch_L,w	;
		movwf	PixelWidth		; W = PixelWidth = NewPPitch_L * 4 / 8
		clrc
		rrf		PixelWidth,f	; PixelWidth = NewPPitch_L * 2 / 8
		clrc
		rrf		PixelWidth,f	; PixelWidth = NewPPitch_L * 1 / 8
		addwf	PixelWidth,f	; PixelWidth = NewPPitch_L * 5 / 8

		bsf		flags2,bDspEnabled ; If bDspEnabled int. routine clears PORTA,bOuterLED

		call	SetOuterLed		; set Port for Outerled equal to flags OuterLED bit

NotON:
		bsf		NewPPitch		; signal new Pitch value to interrupt routine
CalcDone:
		bcf		NewPCnt			; clear flag

		return

;******************************************************************************
;	Tables to display Time and Date
;******************************************************************************

; CHARACTER LOOKUP TABLE    -  Common for Time and Date
; set=LED on, clear=LED off
Char_tbl1
	   	addwf   PCL,f
; Normal characters
TNull
  ifndef	SlashedZero
	DT  0x3E , 0x41 , 0x41 , 0x41 , 0x3E ; "0"
  else
	DT  0x3E , 0x45 , 0x49 , 0x51 , 0x3E ; "0 slashed"
  endif
	DT  0x00 , 0x21 , 0x7F , 0x01 , 0x00 ; "1"
	DT  0x21 , 0x43 , 0x45 , 0x49 , 0x31 ; "2"
	DT  0x42 , 0x41 , 0x51 , 0x69 , 0x46 ; "3"
	DT  0x0C , 0x14 , 0x24 , 0x7F , 0x04 ; "4"
	DT  0x72 , 0x51 , 0x51 , 0x51 , 0x4E ; "5"
	DT  0x1E , 0x29 , 0x49 , 0x49 , 0x06 ; "6"
	DT  0x40 , 0x47 , 0x48 , 0x50 , 0x60 ; "7"
	DT  0x36 , 0x49 , 0x49 , 0x49 , 0x36 ; "8"
	DT  0x30 , 0x49 , 0x49 , 0x4A , 0x3C ; "9"
TDot
	DT  0x00 , 0x36 , 0x36 , 0x00 , 0x00 ; ":"

; Flipped characters - Mirrored and upside-down
DNull
  ifndef	SlashedZero
	DT  0x3E , 0x41 , 0x41 , 0x41 , 0x3E ; "0"
  else
	DT  0x3E , 0x51 , 0x49 , 0x45 , 0x3E ; "0 slashed"
  endif
	DT  0x00 , 0x40 , 0x7F , 0x42 , 0x00 ; "1"
	DT	0x46 , 0x49 , 0x51 , 0x61 , 0x42 ; "2"
	DT	0x31 , 0x4B , 0x45 , 0x41 , 0x21 ; "3"
	DT	0x10 , 0x7F , 0x12 , 0x14 , 0x18 ; "4"
	DT	0x39 , 0x45 , 0x45 , 0x45 , 0x27 ; "5"
	DT	0x30 , 0x49 , 0x49 , 0x4A , 0x3C ; "6"
	DT	0x03 , 0x05 , 0x09 , 0x71 , 0x01 ; "7"
	DT	0x36 , 0x49 , 0x49 , 0x49 , 0x36 ; "8"
	DT	0x1E , 0x29 , 0x49 , 0x49 , 0x06 ; "9"
DMinus
	DT	0x00 , 0x08 , 0x08 , 0x08 , 0x00 ; "-"
; Flipped space looks like normal one  -  Rotation display blanks using this code
DSpace
	DT	0x00 , 0x00 , 0x00 , 0x00 , 0x00 ; " "
Char_tbl1_end

	IF high(Char_tbl1) != high(Char_tbl1_end)
		ERROR "Table1 page error"
    ENDIF

;******************************************************************************
; Load BCD and w with pointer to char table of selected digit  - common for all digital data

LoadBCDData						; load BCD with digital data digits
BCDDataTable					; Computed goto to selected digit of selected table part
		addwf	PCL,f			; Must not set C

WDayTable						; WDay part
;0--
	ifdef	RotationMeas
		movf	RotBCD,w		; Rot low BCD  1 digit
		goto	l_BCD			;
	else
		movlw	DSpace-DNull	; ' '
		goto	l_dot			;
	endif
;1--
	ifdef	RotationMeas
		movf	RotBCD,w		; Rot low BCD 10 digit
		goto	l_BCD_swap		;
	else
		movlw	DSpace-DNull	; ' '
		goto	l_dot			;
	endif
;2--
	ifdef	RotationMeas
		movf	RotBCD100,w		; Rot high BCD  1 digit
		goto	l_BCD			;
	else
		movlw	DSpace-DNull	; ' '
		goto	l_dot			;
	endif
;3--
	ifdef	RotationMeas
		movf	RotBCD100,w		; Rot high BCD 10 digit
		goto	l_BCD_swap		;
	else
		incf	WDay,w			; Day of week
		goto	l_convert		;
	endif
;4--
		movlw	DSpace-DNull	; ' '
		goto	l_dot			;
;5--
		movlw	DSpace-DNull	; ' '
		goto	l_dot			;
;6--
	ifdef	RotationMeas
		incf	WDay,w			; Day of week
		goto	l_convert		;
	else
		movlw	DSpace-DNull	; ' '
		goto	l_dot			;
	endif
;7--
		movlw	DSpace-DNull	; ' '
		goto	l_dot			;

DateTable						; Date part
	ifdef DateFormat_YYMMDD
;8--
		movf	Day,w			; Day     1 digit
		goto	l_convert		;
;9--
		movf	Day,w			; Day    10 digit
		goto	l_convert_swap	;
	else
;8--
		movf	Year,w			; Year    1 digit
		goto	l_convert		;
;9--
		movf	Year,w			; Year   10 digit
		goto	l_convert_swap	;
	endif
;10--
		movlw	DMinus-DNull	; '-'
		goto	l_dot			;
;11--
		movf	Month,w			; Month   1 digit
		goto	l_convert		;
;12--
		movf	Month,w			; Month  10 digit
		goto	l_convert_swap	;
;13--
		movlw	DMinus-DNull	; '-'
		goto	l_dot			;

	ifdef DateFormat_YYMMDD
;14--
		movf	Year,w			; Year    1 digit
		goto	l_convert		;
;15--
		movf	Year,w			; Year   10 digit
	else
;14--
		movf	Day,w			; Day     1 digit
		goto	l_convert		;
;15--
		movf	Day,w			; Day    10 digit
	endif

l_convert_swap					; Convert to BCD and swap nibbles
		call	Conv2BCD		; Convert to BCD, W <= .99, returns with C = 0
l_BCD_swap
		movwf	BCD				; Swap nibbles
		swapf	BCD,w			;
		goto	l_BCD			; High data nibble is now in low BCD nibble

TimeTable						; Time part
;0--
		movf	Hour,w			; Hour   10 digit
		goto	l_convert_swap	;
;1--
		movf	Hour,w			; Hour    1 digit
		goto	l_convert		;
;2--
		movlw	TDot-TNull		; ':'
		goto	l_dot			;
;3--
		movf	Minute,w		; Minute 10 digit
		goto	l_convert_swap	;
;4--
		movf	Minute,w		; Minute  1 digit
		goto	l_convert		;
;5--
		movlw	TDot-TNull		; ':'
		goto	l_dot			;
;6--
		rrf		Second2,w		; Second 10 digit
		goto	l_convert_swap	;
;7--
		rrf		Second2,w		; Second  1 digit

l_convert
		call	Conv2BCD		; Convert to BCD, W <= .99, returns with C = 0
l_BCD
		andlw	0x0F			; Remove high nibble
l_mult5							; C must be 0
		movwf	BCD				; BCD * 1
		rlf		BCD,f			; BCD * 2
		rlf		BCD,f			; BCD * 4
		addwf	BCD,w			; W = BCD * 5
l_dot
		movwf	BCD				; Store pointer to table
		return

BCDDataTableEnd

	IF high(BCDDataTable) != high(BCDDataTableEnd)
		ERROR "BCDDatTable page error"
    ENDIF

;******************************************************************************
;	Check correct progress of sec, min, hour, day, day of week, month, year
;******************************************************************************

TimeCheck
		movlw	Second2
		movwf	FSR
								; FSR -> Second2
		movlw	.120			; Check for second overflow
		call	CheckAndIncNext
								; FSR -> Minute
		movlw	.60				; Check for minute overflow
		call	CheckAndIncNext
								; FSR -> Hour
		movlw	.24				; Check for hour overflow
		call	CheckAndIncNext
								; FSR -> Day
		btfsc	STATUS,C        ; if Day incremented increment WDay as well
		incf	WDay,f

		movlw	.7				; if Day of week == 7
		subwf	WDay,w
		btfsc	STATUS,C        ; if Day of week < 7 then c=0 (borrow)
		movwf	WDay			; Day of week = 0

		movlw	.32				; DMon = 32
		movwf	DMon
		movf	Month,w			;
		movwf	Scratch			; For months <8 odd  months have 31 days
		btfsc	Month,3
		incf	Scratch,f		; For months >7 even months have 31 days
		btfss	Scratch,0
		decf	DMon,f
		xorlw	.2				; Month = February ?
		btfss	STATUS,Z		;
		goto	lNotFeb2		; continue
		decf	DMon,f			; February has max 29 days
		movf	Year,w
		andlw	0x03
		btfss	STATUS,Z
		decf	DMon,f			; Year is not a LeapYear, dec DMon for Feb

lNotFeb2
		movf	DMon,w
		call	CheckAndIncNext	; Check for day overflow
		btfsc	STATUS,C        ; if day cleared, correct day
		incf	Day,f
								; FSR -> Month
		movlw	.13				; Check for month overflow
		call	CheckAndIncNext
		btfsc	STATUS,C        ; if month cleared, correct month
		incf	Month,f
								; FSR -> Year
		movlw	.100			;
		subwf	Year,w			; Year < 100 ?
		btfsc	STATUS,C		;
		clrf	Year			;

		bcf		NewTime			; Clear new time flag
		return

CheckAndIncNext
		subwf	INDF,w			; test INDF value to w
		btfsc	STATUS,C        ; if INDF < w then c=0 (borrow)
		movwf	INDF			; Reset value
		incf	FSR,f			; Move to next unit
		btfsc	STATUS,C        ; If reseted, increment next unit
		incf	INDF,f
		return					; C = 1, if carry was to next unit


;******************************************************************************
;	Routines to display Time and Date
;******************************************************************************

;******************************************************************************
;-------- Update Display memory with Hands

Hands
		movlw	.30				; difference in offset for hands and digital display
		subwf	dpi,w			; scratch4 = dpi - 30
		btfss	STATUS,C		; if scratch4 < 0 then
		addlw	.120			;    scratch4 = scratch4 + 120
		movwf	Scratch4		;

		movf	Second2,w		; Get seconds
		andlw	0xFE			; filter out 1/2 second

		subwf	Scratch4,w
	ifdef	ArcSecMode
ArcSec							; Draw arc if Scratch4 <= (Second2 & 0xFE)

		btfss	flags,bSecMode	; Set C if dot second hand mode
		bsf		STATUS,C
		btfss	STATUS,C
		bsf		tdd,bSecLED		; Arc second hand
	endif
SecHand
		btfsc	STATUS,Z		;
		bsf		tdd,bSecLED		; Dot second hand

MinuteHand
		bcf		STATUS,C		; Clear C
		rlf		Minute,w		; W = Minute * 2
		movwf	Scratch			; Scratch = 2 * Minute
		xorwf	Scratch4,w		;
		movlw	B'10001111'		; Turn on Analog inner led and 4 digital led for minute hand
		call	l_inner

lhour
		rlf		Hour,w			; rlf Minute cleared C, because Minute < .60
		movwf	Scratch2		; W = Scratch2 = Hour * 2
		rlf		Scratch2,f		; Scratch2 = Hour * 4
		rlf		Scratch2,f		; Scratch2 = Hour * 8
		addwf	Scratch2,f		; Scratch2 = Hour * 10
		movlw	.12				;
l_hour_div12
		subwf	Scratch,f		;
		btfss	STATUS,C		;
		goto	l_div12_done	;
		incf	Scratch2,f		; advance hour a bit
		goto	l_hour_div12	;

l_div12_done
		movlw	.120			; Scratch2 = (10 * Hour) + (2 * Minute / 12)
l_120
		subwf	Scratch2,f		;
		btfsc	STATUS,C		; result > 0 ?
		goto	l_120
		addwf	Scratch2,w		; result was negative, re-add 120 and load it in w
		xorwf	Scratch4,w		;
		movlw	B'10000001'		; Trun on analog inner led and 1 digital led for hour hand
l_inner
		btfsc	STATUS,Z		;
OrToTdd
		iorwf	tdd,f			; Led data stored in tdd
		return

;******************************************************************************
;-------- Update Display memory with Time Ticks every 5 minutes: all tick ' / all tick " but at 12'o clock "

Ticks
		movf	dpi,w			; Dpi == tick position ?
		xorwf	Scratch3,w		;
		btfss	STATUS,Z		;
		return
		bsf		tdd,bTickLED	; Draw '
								; Set next tick position
	ifndef	DoubleTickAt12
		movlw	.10				; Draw ' at all position
		addwf	Scratch3,f		; set next tick @
		return

	else
								; Dpi=30 at 12 o'clock
		movf	Scratch3,w
		xorlw	.20
		btfsc	STATUS,Z		; At dpi=20 next position is 29, add 9
		goto	Ticks9
		xorlw	.20^.29			; At dpi=29 next position is 31, add 2
		btfsc	STATUS,Z
		goto	Ticks2
		xorlw	.29^.31			; At dpi=31 next position is 40, add 9
		btfss	STATUS,Z
Ticks10							; Add 10 to position
		movlw	.1
Ticks9							; Add  9 to position, W = 0 if jump to here
		addlw	.7
Ticks2							; Add  2 to position, W = 0 if jump to here
		addlw	.2
		addwf	Scratch3,f
		return
	endif

;******************************************************************************
; Digital display routines

;-------- Update Display memory with Digital Date Display

DDate
		movlw	.66				; Date display is on bottomn side, dpi > 60
		goto	l_DTime_0

;-------- Update Display memory with Digital Time Display

DTime
		movlw	.6				; Time display is on top side dpi < 60
		btfsc	dpi,6			; Return if on date display
		return

l_DTime_0						; Common rutine to display digital date-time
		subwf	dpi,w
		btfss	STATUS,C		;				Time		Date
		return					; display index < 6			< 66
		btfss	STATUS,Z		;
		goto	l_DTime_1		; display index > 6			> 66

								; C=1

		clrf	digitindex		; display index = 6			= 66
		clrf	dotindex		; load start parameters for digit displaying

l_DTime_1
		btfsc	digitindex,3
		return					; If digitindex >= 8 don't display anything

		movlw	high(WDayTable) ; Prepare PCLATH to access tables
		movwf	PCLATH			; PCLATH values for WDayTable and Char_tbl1 are the same

		movf	dotindex,w		; C = 1
		btfss	STATUS,Z		;
		goto	l_DTime_3
								; dotindex = 0
		movlw	6				; Init dotindex
		addwf	dotindex,f		; C = 0

		rlf		digitindex,w	; Two instructions/digit, Clears C

		btfsc	dpi,6			; dpi > 64 for Date display
		goto	l_DDate_2

		addlw	TimeTable-WDayTable; Use time part of table
		call	LoadBCDData		; Load new digit, returns BCD in w
		iorwf	digitindex,w	; Can only be 0 if digitindex = 0 and BCD value = 0
		movlw	DSpace-TNull	; 10 hour digit is zero, remove leading zero using flippes space
		btfsc	STATUS,Z		;
		movwf	BCD				; BCD = " "
		goto	l_DTime_3

l_DDate_2						; WDay part of table starts at 0
		btfss	Second2,3		; if ((2*second) & 8)==8 digits from WDay and Rotation
		addlw	DateTable-WDayTable; Use date part of table
		btfss	ShowWDay		; if Day of week display disabled use Date part of table
		iorlw	DateTable-WDayTable; Show Date always
		call	LoadBCDData		; Load new digit, returns BCD in w
		addlw	DNull-TNull		; use flipped table
		movwf	BCD

l_DTime_3
		decfsz	dotindex,f		; Move to next column
		goto	l_DTime_2		; dotindex > 0, display data
								; dotindex = 0, don't display anything
		incf	digitindex,f	; select next digit
		return					; to get a gap between digits

l_DTime_2
	if high(WDayTable)!=high(Char_tbl1)
		movlw	high(Char_tbl1)	; Init PCLATH for Char_tbl1 table access
		movwf	PCLATH
	endif
		movf	BCD,w			;
        call    Char_tbl1   	; get the dot pattern for this column
		incf	BCD,f			; Move table pointer
		goto	OrToTdd			; Store data to tdd

;******************************************************************************
;	Processing of RC5 command's, called by main program if a command is received
;******************************************************************************

ProcessRC5
	ifdef	RC5AddrCheck
		movf	RC5_Addr,w		;
		xorlw	RemoteAddr		; test if RC5_Addr = RemoteAddr
		andlw	0x1F			; Only 5 bits to test
		btfss	STATUS,Z		;
		goto	ProcessRC5Done	;
	endif

		movf	RC5_Cmd,w		;
		andlw	0x7F			; Mask toggle bit

		select_w
		case	SEC_CL			;
		  goto	ClrSecond		; Adjust time : Clear Seconds
		case	SEC_UP			;
		  goto	IncSecond		; Adjust time : Increment Seconds
		case	SEC_DN			;
		  goto	DecSecond		; Adjust time : Decrement Seconds
		case	MIN_UP			;
		  goto	IncMinute		; Adjust time : Increment Minutes
		case	MIN_DN			;
		  goto	DecMinute		; Adjust time : Decrement Minutes
		case	HOUR_UP			;
		  goto	IncHour			; Adjust time : Increment Hours
		case	HOUR_DN			;
		  goto	DecHour			; Adjust time : Decrement Hours
		case	DAY_UP			;
		  goto	IncDay			; Adjust Date : Increment Days
		case	DAY_DN			;
		  goto	DecDay			; Adjust Date : Decrement Days
		case	WDAY_UP			;
		  goto	IncWDay			; Adjust Date : Increment Day of week
		case	WDAY_DN			;
		  goto	DecWDay			; Adjust Date : Decrement Day of week
		case	MON_UP			;
		  goto	IncMonth		; Adjust Date : Increment Month
		case	MON_DN			;
		  goto	DecMonth		; Adjust Date : Decrement Month
		case	YEAR_UP			;
		  goto	IncYear			; Adjust Date : Increment Year
		case	YEAR_DN
		  goto	DecYear			; Adjust Date : Decrement Year
		case	INDEX_UP		;
		  goto	DecDispOffset	; Adjust index sensor Offset, rotate display left
		case	INDEX_DN		;
		  goto	IncDispOffset	; Adjust index sensor Offset, rotate display right

								; Toggle functions
		movf	RC5_Cmd,w		;
		xorwf	RC5_Cmd2,f		; Compare command with toggle bit
		btfsc	STATUS,Z		;
		goto	ProcessRC5Done	;
		andlw	0x7F			; New command, remove toggle bit

		select_w
		case	BlueLine 		;
		  goto	ToggleOuterLed	; Toggle Outer LED
	ifdef	ArcSecMode
		case	TSecMode		;
		  goto	ToggleSecMode	; Toggle Arc / Dot second hand mode
	endif
		case	DigiTime		;
		  goto	ToggleTime		; Digital Time
		case	DigiDate		;
		  goto	ToggleDate		; Digital Date
		case	TWDay			;
		  goto	ToggleWDay		; Day of week display
		case	AnaTime			;
		  goto	ToggleAnalog	; Analog Time
		case	TTicks			;
		  goto	ToggleTick		; Analog Time Ticks
		case	DemoM			;
		  goto	ToggleDemo		; Demo Mode
	ifdef	RotationMeas
		case	TRotation		;
		  goto	ToggleRot		; Rotation data
	endif
		case	TextMode		;
		  goto	ToggleText		; Scrolling Text Mode

	ifdef	StaticText
		case	TStaticText		;
		  goto	ToggleStaticText; Static Text Mode
	endif
		case	SET_SP			;
		  goto	SetScrollSp		; Set Scrolling speed
		case	STANDBY			;
		  bsf	fDspEnabled		; Disable LEDs
;		default					; If none of listed codes
		goto	ProcessRC5Done	;

ToggleOuterLed
	ifdef	UsePCF8583RTC
								; Afther setting time toggle OuterLed will store time to RTC
		btfsc	RC5_TimeSet		; If time modified
		call	StoreTime		; Write it into RTC
		bcf		RC5_TimeSet		; Clear modification flag
	endif
		movlw	1 << bOuterLED	; Toggle bit in flags
		goto	XorToFlags		;

	ifdef	RotationMeas
ToggleRot
		movlw	1 << bShowRot	; Toggle bit in flags
		goto	XorToFlags		;
	endif

ToggleWDay
		movlw	1 << bShowWDay	; Toggle bit in flags
		goto	XorToFlags		;

ToggleTime
		movlw	1 << bShowDTime	; Toggle bit in flags
		goto	XorToFlags		;

ToggleDate
		movlw	1 << bShowDDate	; Toggle bit in flags
XorToFlags
		xorwf	flags,f			;
FlagsToRTC						; Save flags settings to RTC ram
	ifdef	UsePCF8583RTC
		movlw	RTC_flags		; Address of Flags
		movwf	digitindex
		movf	flags,w			; Get new flags to store
		call	I2CByteWrite	; Store it in RTC
	endif
		goto	ProcessRC5Done	;

ToggleAnalog
		movlw	1 << bShowHand	; Toggle bit in flags
		goto	XorToFlags		;

ToggleTick
		movlw	1 << bShowTicks	; Toggle bit in flags
		goto	XorToFlags		;

	ifdef	ArcSecMode
ToggleSecMode
		movlw	1 << bSecMode	; Toggle bit in flags
		goto	XorToFlags		;
	endif

IncDispOffset
		incf	DispOffset,w	; Increment offset
		call 	CheckIncrement 	; Check address
		goto	SetDispOffs		;

DecDispOffset
		decf	DispOffset,w	; Decrement offset
		call 	CheckDecrement	; Check address
SetDispOffs
		movwf	DispOffset		; Store new value
	ifdef	UsePCF8583RTC
		call	DispOffsToRtc	; Save it to RTC
	endif
		goto	ProcessRC5Done	;

SetScrollSp
		movlw	0x10			; Set scrolling speed
		addwf	CCPR1H,w
		iorlw	0xC0			; Speeds are 0xCF, 0xDF, 0xEF, 0xFF
		movwf	CCPR1H
		goto	ProcessRC5Done	;

IncSecond
		incf	Second2,f		; Inc seconds
		incf	Second2,f		;
		movlw	.120			;
		subwf	Second2,w		;
		btfsc	STATUS,C		;
ClrSecond						; Clear seconds
		clrf	Second2			;
		goto	TimeSetRC5Done	;

DecSecond
		movlw	.2				; Dec seconds
		subwf	Second2,f		;
		movlw	.120			;
		btfss	STATUS,C		;
		addwf	Second2,f		;
		goto	TimeSetRC5Done	;

IncMinute
		incf	Minute,f		; Inc minute
		movlw	.60				;
		xorwf	Minute,w		;
		btfsc	STATUS,Z		;
StoreMinute
		movwf	Minute			;
		goto	TimeSetRC5Done

DecMinute
		decf	Minute,f		; Dec minute
		btfss	Minute,7
		goto	TimeSetRC5Done	;
		movlw	.59
		goto	StoreMinute

IncHour
		incf	Hour,f			; Inc hour
		movlw	.24				;
		xorwf	Hour,w			;
		btfsc	STATUS,Z		;
StoreHour
		movwf	Hour			;
		goto	TimeSetRC5Done	;

DecHour
		decf	Hour,f			; Dec hour
		btfss	Hour,7
		goto	TimeSetRC5Done	;
		movlw	.23
		goto	StoreHour

IncDay
		decf	DMon,w			; Inc day
		xorwf	Day,w			;
        btfsc	STATUS,Z		;
		clrf	Day				;
		incf	Day,f			;
		goto	TimeSetRC5Done	;

DecDay
		decfsz	Day,f			; Dec day
		goto	ProcessRC5Done	;
		decf	DMon,w			;
		movwf	Day				;
		goto	TimeSetRC5Done	;

IncWDay
		incf	WDay,f			; Inc day of week
		movlw	.7
		xorwf	WDay,w
 	    btfsc	STATUS,Z		;
StoreWDay
		movwf	WDay			;
		goto	TimeSetRC5Done	;

DecWDay
		decf	WDay,f			; Dec day of week
		btfss	WDay,7
		goto	TimeSetRC5Done	;
		movlw	.6
		goto	StoreWDay

IncMonth
		movlw	.12				; Inc month
		xorwf	Month,w			;
		btfsc	STATUS,Z		;
StoreMonth
		movwf	Month			;
		incf	Month,f			;
		goto	TimeSetRC5Done	;

DecMonth
		decfsz	Month,f			; Dec month
		goto	TimeSetRC5Done	;
		movlw	.11				;
		goto	StoreMonth		;

DecYear							; Dec year
		decf	Year,f
		movlw	.99				;
		btfsc	Year,7
		movwf	Year
		goto	TimeSetRC5Done	;

IncYear							; Increment year -> TimeCheck will correct
		incf	Year,f

TimeSetRC5Done
		bcf		fTimeInv		; Clear time invalid
		bsf		RC5_TimeSet		; Set time set command executed flag
		bsf		NewTime			; force display update
		goto	ProcessRC5Done	;

	ifdef	StaticText
ToggleStaticText				; Static text
		bcf		fScrollOn		; Scrolling OFF
		movlw	1 << bText		; toggle Text flag
		xorwf	flags2,f		;
		btfsc	fText
		call	PrintDisp		; Print static message
		goto	ProcessRC5Done	;
	endif

ToggleText
		call	TextON_OFF		; Scrolling Text mode
		goto	ProcessRC5Done	;

ToggleDemo
		movlw	1 << bDemo		; Demo mode
		xorwf	flags2,f		;

ProcessRC5Done
		movf	RC5_Cmd,w		; Store this command code to previous command
		movwf	RC5_Cmd2		;
		bcf		RC5_DataReady	; Data processed
SetOuterLed
	ifdef	UsePCF8583RTC
		bcf		CCPR1L,bOuterLED; Set OuterLED as in flags
		btfsc	ShowOuter		;
		bsf		CCPR1L,bOuterLED;
		bcf		INTCON,GIE		; Disable interrupts
		call	ToPorta			; Changing PORTA is a critical oreration, PORTA and it's copy has to be the same everytime
		bsf		INTCON,GIE		; Enable  interrupts
	else
		btfss	ShowOuter		; Set OuterLED as in flags
		bcf		OuterLED		;
		btfsc	ShowOuter		;
		bsf		OuterLED		;
	endif
		return					;

;******************************************************************************
; Display mode routines

; Change scrolling text / Time-Date display mode
DemoMode
		movf	Second2,f		; Check for demo mode
		btfss	STATUS,Z		;
		return

; Switch text mode on / off
TextON_OFF
		bcf		fScrollOn		; Scrolling OFF
		movlw	1 << bText		; toggle Text flag
		xorwf	flags2,f		;
		btfss	fText			; test Text flag
		return					;
		bsf		fScrollOn		; Scrolling ON

; Clear display memory
ClearDisplay

		bsf		STATUS,RP1		; Bank2

		movlw	0x20			; set FSR to begining of display buffer
		movwf	FSR				;
		movlw	.120			; There are 120 positions
		movwf	ch_dot_index
cldisp
		clrf	INDF			; clear display memory
		call	CheckIncrementFSR; Increment and test pointer
		movwf	FSR
		decfsz	ch_dot_index,f	; Loop for .120 positions
		goto	cldisp			; Loop clears ch_dot_index

								; init stuff needed for Text Scroll function
   		clrf	ch_blanking		; Variables are in common RAM

	if	high(EEADR)!=high(ch_blanking)
		banksel	EEADR			; Bank1
	endif

	if	EEPROM_SIZE==256
		movlw	0x80			; If EEPROM_SIZE==256 there are two messages
		andwf	EEADR,f			; Clear address for next read - keep message index
	else
		clrf	EEADR			; Clear address for next read
	endif

		goto	RetBank0		; Back to Bank0 and Return data for TMR2 initialization

	ifdef	RotationMeas
;******************************************************************************
;	Convert rotation counter to BCD
;******************************************************************************

; Get rotation data convert it to BCD - Converted BCD here because balanking
ConvRot
		bcf		NewRot			; Clear new rotation data flag

		btfss	ShowRot			; If on dislplay
		goto	RotBlank

		movf	Rotation_H,w	; Get rotation measurement
		movwf	DIV_HI
		movf	Rotation_L,w	; Display content not valid over 9999 rot/minute
		movwf	DIV_LO

		movlw	.100
		call	div16X8Divisor	; Divide by 100
		andlw	0x7F			; Max 127 - can be handled by Conv2BCD
		call	Conv2BCD		; Convert to BCD
		movwf	RotBCD100		; Store quotient 100 r/m
		btfsc	RotBCD100,7		; Prevent table boundary violation
		bcf		RotBCD100,6
		movf	DIV_HI,w		; Remainder <= .99
		call	Conv2BCD		; Convert to BCD
		goto	RotSave			; Store remainder  1 r/m

; Blank rotation BCD data using DSpace character
RotBlank
		movlw	0xBB			; Blank the rotation dislpay
		movwf	RotBCD100
RotSave							; Store r/m unit
		movwf	RotBCD
		retlw	0xFF
	endif

;******************************************************************************
;	Main program
;******************************************************************************

main
InitIO
;;;		clrf	PORTA			; All LED's OFF, done at reset vector
;;;		clrf	PORTB
;;;		clrf	CCPR1L			; Clear copy of PORTA as well, done at reset vector

		bsf		STATUS,RP0		; Bank1

		movlw	0xFF^((1<<bPixel0)|(1<<bOuterLED)|(1<<bCalibration))			;
		movwf	TRISA			; set bit 7..4, 2 input/ 3, 1..0 output
		movlw	1<<bIndexPuls	; set bit 0 input/ 7..1 output
 		movwf	TRISB			; LEDs will be turned on if rotation is stable

;;;		clrf	PIE1			; dissable all possible interrupt enable - cleared at any reset
		movlw	b'10011000'		; set up timer. prescaler(bit3)bypassed, Int on falling Edge RB0
		movwf	OPTION_REG		;
		movlw	.249			; TMR2 PR2=249 - divide by 1000
		movwf	PR2				;
		bsf		PIE1,TMR2IE		; enable Peripheral interrupt from TMR2

	ifdef	__16F88
		clrf	ANSEL			; On 16F88 use all PORTA pins as digital I/O, A/D is off afther reset
	endif

		bcf		STATUS,RP0		; Bank0

InitRam
		call	InitSubSecCnt	; Returns value to comparator initialization

	if	high(CMCON)==1
		bsf		STATUS,RP0		; Bank1 for 16F87 and 16F88, Voltage reference modul is off afther reset
	endif	
		movwf	CMCON			; Turn comparators OFF
	if	high(CMCON)==1
		bcf		STATUS,RP0		; Bank0
	endif	

	ifdef	RotationMeas
		call	RotBlank		; Blanks rotation BCD data using flipped Spaces, returns Slowest scrolling speed
	else
		movlw	0xFF			; Slowest scrolling speed
	endif
		movwf	CCPR1H			; Init scrolling speed

		clrf	PixelWidth		;
		clrf	PixelOff		;

	ifdef	UsePCF8583RTC
		call	InitTime		; Init time from RTC
	else
		call	InitTimeDef		; Init time with default data
	endif

		movf	DispOffset,w	; Init FSR of interrupt routine
		movwf	iFSR			;

		clrf	flags2			; Clear system flags
		clrf	RC5_flags
		bsf		RC5_WaitStart	; RC5 is waiting for startbit
		clrf	RC5_Tmr			;

InitTmr
		call 	ClearDisplay	; clear display content, returns value to init T2CON 0x05
		movwf	T2CON			; TMR2=ON, Prescaler = 1/4, Postscaler = 1, PR2 = 249, Divide by 1000

;;;		clrf	T1CON			; Init flags in T1CON - All resets clear these bits

		clrf	INTCON			; clear all possible interrupt flag - only RBIF not cleared at reset
		clrf	PIR1			; clear TMR2 interrupt request
		movlw	(1<<T0IE) | (1<<INTE) | (1<<PEIE) | (1<<GIE)
		movwf	INTCON			; Enable interrupts

		clrf	TMR0			; restart timer

MainLoop
		btfsc	RC5_DataReady	; If new command received
		call	ProcessRC5		;  Process remote control command

TestScrolling
		btfss	fScrollOn		; If scrolling text on display
		goto	NoScrolling		;
		movf	TmrScroll,f		; Test scrolling time
		btfss	STATUS,Z		;
		goto	NoScrolling		;
		movf	CCPR1H,w		; Get Scrolling speed
		movwf	TmrScroll		; Reinit scrolling timer
		call	ScrollText		; Scroll the display

NoScrolling
		btfsc	NewPCnt			; test Period counter flag
		call	CalcPixelPitch  ; calculate new pixel pitch

		btfss	NewTime			; test new time flag
		goto	MainLoop		;

 		call	TimeCheck		; half second past, do TimeCheck

	ifdef	RotationMeas
		btfsc	NewRot			; Rotation measurement ready
		call	ConvRot			; Convert it and store BCD data
	endif

		btfsc	fDemo			; Test for Demo mode active
		call	DemoMode		; Check Second2 to toggle Time-Date / Scrolling text display

		btfsc	fText			; test Text flag
		goto  	MainLoop		; Text on display, leave display-content unchanged

								; start display memory update
		clrf	dpi				; dpi = 0
		clrf	Scratch3		; keeps index for timeticks

		movlw	0x20			; start of display memory

lUpdateDisplay					; Build display data in buffer
		movwf	FSR
		clrf	tdd				; Clear temporary display data for flicker free operation

		btfsc	ShowDTime		; If digital Time on
		call	DTime			; Display digital Time

		btfsc	ShowDDate		; If digital Date on
		call	DDate			; Display digital Date

		btfsc	ShowHand		; If Hands on
		call	Hands			; Analogue clock's hands

		btfsc	ShowTicks		; If Ticks on
		call	Ticks			; Analogue clock's Ticks

		movf	tdd,w			; store data in display memory
		movwf	INDF			;

		incf	dpi,f			; Increment display index
		movlw	.120			;
		xorwf	dpi,w			;
		btfsc	STATUS,Z		; Check end of buffer
		goto  	MainLoop		;

		call	CheckIncrementFSR ; Move pointer
		goto	lUpdateDisplay	; loop to update next position

;******************************************************************************
;	Text display functions
;  Scratch and ch_* variables are Bank2
;******************************************************************************

;******************************************************************************
; Get pointer to CharTab table for character in W  --- Bank2

CharOffset
		movwf	ch_dot_point_L		; store ascii_code
		clrf	ch_dot_point_H		; clear high byte
		movlw	.32					; ascii_code - .32
		subwf	ch_dot_point_L,f	;

		btfsc	ch_dot_point_L,7	; (ascii_code - .32) > .127 means invalid code,
		clrf	ch_dot_point_L		; relpace with " "

		movlw	.6					; Called only when ch_dot_index == 0
		addwf	ch_dot_index,f		; indicate start of new char, clears C

		rlf		ch_dot_point_L,f	; ch_dot_point_L = (ascii_code - .32) * 2, C = 0 because ch_dot_point_L <.128
;;;		rlf		ch_dot_point_H,f	; Why rotate a 0x00 if C=0 [ max 0x7F<<1 == 0xFE , C = 0 ]
									; C = 0
		rlf		ch_dot_point_L,w	; W = (ascii_code - 32) * 4
		rlf		ch_dot_point_H,f	; Save C to high part
		addwf	ch_dot_point_L,f	; ch_dot_point_L = (ascii_code - 32) * 6
		btfsc	STATUS,C			; If C==1, increment high part
		incf	ch_dot_point_H,f	; ch_dot_point (16bit) = (ascii_code - 32) * 6
		return

;******************************************************************************
; Get dot pattern of character last processed with CharOffset  --- Bank2

LoadChrData
		movf	ch_dot_point_H,w	; Get high path of index
		addlw	high(CharTab)		; Add address of char gen table
		movwf	PCLATH				; Init PCLATH for table access
		movf	ch_dot_point_L,w	; Get low  part of index
		call	CharGen				; Convert to character's dot pattern
		incf	ch_dot_point_L,f	; pointer++
		btfsc	STATUS,Z			; If owerflow
		incf	ch_dot_point_H,f	;  increment high part (J and u are on boundary)
		return


	ifdef	StaticText
;******************************************************************************
; Display static text

PrintDisp
		call	ClearDisplay		; clear display content, clears ch_dot_index

	if	(EEADR & 0x080)==0x080
		bsf		STATUS, RP0			; Bank1
	endif
	if	(EEADR & 0x100)==0x100
		bsf		STATUS, RP1			; Bank2
	endif

		movlw	0x70
		movwf	EEADR				; start of static message in EEProm
		movlw	0xE1				; Start for first char a 7 o'clock
		movwf	FSR					;
		movlw	.15					; 15 char to display
		movwf	Scratch3			;
pdisp_1
		call	EpromRead			; Read next character from EEProm, returns with Bank2 selected
		call	CharOffset			; call character data

pdisp_2
		call	LoadChrData			; load pixel data from CharGen
		movwf	INDF				; store char pixel data in display memory
		call 	CheckIncrementFSR	; increment FSR
		movwf	FSR					;
		decfsz	ch_dot_index,f		; 6 dot's in one character, all displayed ?
		goto	pdisp_2				;

		decfsz	Scratch3,f			; Loop for 15 characters
		goto	pdisp_1

		goto	RetBank0
	endif

;******************************************************************************
; Display scrolling text

ScrollText
		bsf		STATUS,RP1			; Bank2

      	movf	ch_dot_index,w		;
		btfss	STATUS,Z			; ch_dot_index == 0 ?
		goto	Scroll_0			; No - process next dot pattern

		movf	ch_blanking,w		; ch_blanking == 0 ?
		btfsc	STATUS,Z			;
		goto	Scroll_read_ee		; Yes - Read character form EEProm
		decfsz	ch_blanking,f		;
		goto	Scroll_2			; insert one more " "
									; ch_blanking == 0 - End of message and leadout
	if	high(EEADR)!=high(ch_blanking)
		banksel	EEADR				; Bank1
	endif

	if	EEPROM_SIZE==256
		movlw	0x80				; On 16F684A, 16F87, 16F88 change message
		andwf	EEADR,f
		xorwf	EEADR,f
	else
		clrf	EEADR				; On 16F628(A) Go to beginning of message
	endif

		btfss	fDemo				; in demo mode?
		goto	Scroll_read_ee		; No: re-read char

		clrf	STATUS				; Bank0
		goto	TextON_OFF			; at end of line, turn text off

Scroll_read_ee
		call	EpromRead			; Read next character from EEProm, returns with Bank2 selected
		btfss	STATUS,Z			; 0x00 indicates end of line
		goto	Scroll_1			;

		movlw	0x0F				; insert 15 " " at end of string to clear display
		movwf	ch_blanking			;

Scroll_2
		movlw	" "					; w = " "

Scroll_1
		call	CharOffset			; call character data
Scroll_0
		call	LoadChrData			; load pixel data from CharGen
		decf	ch_dot_index,f		; dec ch_dot_index
		movwf	Scratch				;

	ifdef ReversTextScroll
		movlw	0xE4				; Start at begin of 7 o'clock character
	else
		movlw	0xBF				; Start at end of 5 o'clock character
	endif
		movwf	FSR					; Init display memory pointer
ScrollLoop
		movf	INDF,w				; Get current display data = Previous_INDF
		xorwf	Scratch,w			; W = (Previous_INDF ^ Scratch)
		xorwf	INDF,f				; INDF = (Previous_INDF ^ Scratch) ^ INDF = Scratch
		xorwf	Scratch,f			; Scratch = (Previous_INDF ^ Scratch) ^ Scratch = Previous_INDF

	ifdef ReversTextScroll
		call	CheckIncrementFSR	; Move pointer
		movwf	FSR					;
		xorlw	0xC0				; check end of 5 o'clock character
	else
		call	CheckDecrementFSR	; Move pointer
		movwf	FSR					;
		xorlw	0xE3				; check begin of 7 o'clock character
	endif
		btfss 	STATUS,Z			; Check for end of area: FSR = 0xE3 (or 0xC0)
		goto	ScrollLoop			;

RetBank0
		clrf	STATUS				; go back to bank 0
		retlw	0x05				; Return data for TMR2 initialization from ClearDisplay

;******************************************************************************
; Reads next character from EEProm memory - returns with Bank2 selection

EpromRead
		banksel	EEDATA				; Select Bank of EEDATA

		bsf		EECON1, RD			; read character from EEProm
		incf	EEADR,f				; inc address for next read

	if	EEPROM_SIZE==128
		bcf		EEADR,7				; on 16F628A roll over after 0x80 => 0x00
	endif

		movf	EEDATA,w			; Get next character

	if high(EEDATA)!=high(ch_dot_index)
		banksel	ch_dot_index		; Bank2
	endif
									; Returns with Bank2 selected
		return

;******************************************************************************
;	Some general functions
;******************************************************************************

;- check correct decrement of Display memory pointer
CheckDecrementFSR
		decf	FSR,w				; Dec FSR to w
CheckDecrement
		xorlw	0x1F				;
		btfsc	STATUS,Z			;
		movlw	0xEF ^ 0x1F			;
		xorlw	0x9F ^ 0x1F			;
		btfsc	STATUS,Z			;
		movlw	0x47 ^ 0x9F			;
		xorlw	0x9F				;
		return						;

;- check correct increment of Display memory pointer
CheckIncrementFSR
		incf	FSR,w				; Inc FSR ro w
CheckIncrement
		xorlw	0x48				;
		btfsc	STATUS,Z			;
		movlw	0xA0 ^ 0x48			;
		xorlw	0xF0 ^ 0x48			;
		btfsc	STATUS,Z			;
		movlw	0x20 ^ 0xF0			;
		xorlw	0xF0				;
		return						;


	ifdef	UsePCF8583RTC
;******************************************************************************
;	I2C RTC functions
;******************************************************************************

;******************************************************************************
; Read the time, flags, display offset from RTC

InitTime						; Init time from I2C RTC
		movlw	RTC_Second		; Address of Seconds
		call	I2CByteReadStoreAddr; Read seconds
		call	BcdToBin
		movwf	Second2
		addwf	Second2,f
								; digitindex = 0x03
		call	I2CByteRead		; Read minute
		call	BcdToBin
		movwf	Minute
								; digitindex = 0x04
		call	I2CByteRead		; Read hour
		call	BcdToBin3F		; Mask format and AM/PM
		movwf	Hour
								; digitindex = 0x05
		call	I2CByteRead		; Read day
		call	BcdToBin3F		; Mask Year bits
		movwf	Day
		swapf	tdd,f
		rrf		tdd,f
		rrf		tdd,w
		movwf	dotindex		; Save year bit 1..0
								; digitindex = 0x06
		call	I2CByteRead		; Read Month
		andlw	0x1F
		call	BcdToBin
		movwf	Month
		swapf	tdd,f
		rrf		tdd,w
		andlw	0x07			; Remove swapped Month
		movwf	WDay
								; digitindex = 0x07

		movlw	0x10			; Address of Year
		call	I2CByteReadStoreAddr; Read year
								; digitindex = 0x11
		movwf	Year
		call	I2CByteRead		; Read year ^ 0xff
								; digitindex = 0x12
		xorlw	0xFF			; Complement read data
		xorwf	Year,w			; Has to be same as Year
		btfss	STATUS,Z
		goto	InitTimeDef		; Year not valid, init with default values

		call	I2CByteRead		; Read flags
		movwf	flags
								; digitindex = 0x13
		call	I2CByteRead		; Read DispOffset
		movwf	DispOffset
								; digitindex = 0x14
		call	ValidateOffs	; Check display offset read from RTC / Rewrite a valid value

		movf	Year,w			; Has the RTC incremented Year
		xorwf	dotindex,w
		andlw	0x03
		btfsc	STATUS,Z
		return					; No, return

		incf	Year,f			; Yes, increment Year
StoreYear
		movlw	RTC_Year		; Address of Year
		movwf	digitindex
		movf	Year,w			; Write Year
		call	I2CByteWrite
								; digitindex = 0x11
		comf	Year,w
		goto	I2CByteWrite	; Write complement of Year

;******************************************************************************
; Validate display offset:
; 0x20 <= Display offset < 0x48 or 0xA0 <= Display offset < 0xF0

ValidateOffs
		movlw	0x20
		subwf	DispOffset,w
		btfss	STATUS,C
		goto	DifDispOffsStore
		movlw	0xF0
		subwf	DispOffset,w
		btfsc	STATUS,C
		goto	DifDispOffsStore
		movlw	0xA0
		subwf	DispOffset,w
		btfsc	STATUS,C
		return					; DispOffset valid
		movlw	0x48
		subwf	DispOffset,w
		btfss	STATUS,C
		return					; DispOffset valid
DifDispOffsStore				; Get default value
		call	DifDispOffs

; Store DispOffset to RTC
DispOffsToRtc
		movlw	RTC_disp_off	; Address of DispOffset
		movwf	digitindex
		movf	DispOffset,w
		goto	I2CByteWrite

	endif

;******************************************************************************
; Init time with default value: 2001-01-01 12:00:00, day of week: 1 ,set time invalid
; Init flags with default value
; Init display offset with default value

InitTimeDef
;;		clrf	Hour 			; May start from 00:00:00
		movlw	.12				; why do clocks always start
		movwf	Hour			; at 12:00 ?
;;
		clrf	Minute
        clrf	Second2
		movlw	.1
		movwf	Day
		clrf	WDay
		movwf	Month
		movwf	Year

		movlw	(1<<bShowHand) | (1<<bShowDTime) | (1<<bShowDDate) | (1<<bShowTicks) | (1<<bShowWDay)
		movwf	flags			; init flags

		bsf		fTimeInv		; Sign time not valid

; Init display offset with default value
DifDispOffs
		movlw	DISPLAYOFFSET	; Initial Display Offset for the hardware
		movwf	DispOffset		;
		return

	ifdef	UsePCF8583RTC

;******************************************************************************
; Convert BCD number to binary - mask bit 7..6 off

BcdToBin3F						; Convert BCD to binary for Hour and Day
		andlw	0x3F

; Convert BCD number to binary
BcdToBin						; Convert BCD to binary for numbers < 60
		movwf	FSR
		andlw	0x0F
		btfsc	FSR,4
		addlw	.10
		btfsc	FSR,5
		addlw	.20
		btfsc	FSR,6
		addlw	.40
		return

;******************************************************************************
; Write the time, flags, display offset to RTC

StoreTime
		movlw	0x80			; Disable counting command
		call	SendRtcCmd
								; digitindex = 0x01
		incf	digitindex,f	; Address of seconds is 2
								; digitindex = 0x02

								; Last carry setting operation was rlf BCD
								; widt bit 1 of command code (0x80) shifted to carry
								; so carry = 0
		rrf		Second2,w		; Store Second = (Seconds2 >> 1)
		call	I2CBcdWrite
								; digitindex = 0x03
		movf	Minute,w		; Store Minute
		call	I2CBcdWrite
								; digitindex = 0x04
		movf	Hour,w			; Store Hour
		call	I2CBcdWrite
								; digitindex = 0x05
		swapf	Year,w			; Store Day and Year bit 1..0
		movwf	tdd
		rlf		tdd,f
		rlf		tdd,f
		movlw	0xC0
		andwf	tdd,f
		movf	Day,w
		call	BCDiorWrite
								; digitindex = 0x06
		swapf	WDay,w			; Store Month and Wday
		movwf	tdd
		rlf		tdd,f
		movlw	0xE0
		andwf	tdd,f
		movf	Month,w
		call	BCDiorWrite
								; digitindex = 0x07
		call	EnableCount
		goto	StoreYear

BCDiorWrite
		call	Conv2BCD
		iorwf	tdd,w
		goto	I2CByteWrite

; Send enable count command to RTC
EnableCount
		movlw	0x00			; Enable counting command

; Send command to RTC
SendRtcCmd
		clrf	digitindex
		goto	I2CByteWrite

;******************************************************************************
;	I2C low level functions
;******************************************************************************

; Temporary variables are in common Ram - Can be used from Bank of TRISA too
; START selects BANK1 to access TRISA   - STOP select BANK0 before return

; At reaet SCL and SDA bits in PORTA and CCPR1L are cleared
; Porta writes are made with copying CCPR1L to PORTA to keep these bits cleared

;******************************************************************************
; Reads a byte from RTC to tdd, address is in w
; store address in digitindex, digitindex incremented afther execution

I2CByteReadStoreAddr
								;
		movwf	digitindex		; Store address

; Reads a byte from RTC to tdd, address is in digitindex
; digitindex incremented afther execution

I2CByteRead
	CALL 	START				; Generate Start, returns with 10100000 RTCADDR in W
	CALL 	OUT_BYTE			; Send slave address byte + nack
	CALL 	OUT_BYTE_ADDR		; Send word  address byte + nack

	CALL 	START				; Generate repeted start
	MOVLW	RTC_ADDR | 1		; output 10100001 for read
	CALL 	OUT_BYTE			; Send byte + nack

IN_BYTE							; Read byte on i2c bus
	CALL 	HIGH_SDA			; Configure SDA as input
	bsf		FSR,3				; Load 8 to FSR, OUT_BYTE celared FSR
IN_BIT
	CALL 	HIGH_SCL			; SCL -> 1 ; 5us wait
	BCF		STATUS,RP0			; Bank 0 to read PORTA
	BCF		STATUS, C			; clear carry
	BTFSC 	SDA					; test SDA bit
	BSF 	STATUS, C			; set carry if SDA == 1
	BSF		STATUS,RP0			; Bank 1 to control TRISA
	RLF 	tdd,F				; tdd = (tdd << 1) | input bit
	CALL 	LOW_SCL				; SCL -> 0 ; 5us wait
	DECFSZ 	FSR, F				; decrement bit counter
	GOTO 	IN_BIT
	CALL	NACK				; Clock out nack bit SDA must be high (input)

ToStop
	CALL 	STOP				; Generate stop condition
	incf	digitindex,f		; Increment word address
	movf	tdd,W				; Return data just read
	bcf		STATUS,RP0			; Back to Bank 0
	return

;******************************************************************************
; Writes W to RTC in BCD format, address is in digitindex
; digitindex incremented afther execution

I2CBcdWrite
	call	Conv2BCD			; Convert w to BCD

; Writes W to RTC, address is in digitindex
; digitindex incremented afther execution

I2CByteWrite
	movwf	tdd					; Save data to be written to RTC
	CALL 	START				; Generate Start, returns with 10100000 RTCADDR in W
	CALL 	OUT_BYTE			; Send slave address byte + nack
	CALL 	OUT_BYTE_ADDR		; Send word  address byte + nack
	MOVF 	tdd, W				; Get output data
	CALL 	OUT_BYTE			; Send data byte + nack
	goto	ToStop

; Generate stop condition on I2C bus
STOP:							; SDA 0 -> 1 while SCL == 1
								; SCL must be LOW afther (N)ACK's CLOCK_PULSE
	CALL	LOW_SDA				; SDA -> 0 - NACK has done it
	CALL	HIGH_SCL			; SCL -> 1 and make 5us stop setup time

; Make SDA high by making it input
HIGH_SDA:						; high impedance by making SDA an input
	BSF 	SDA					; make SDA pin an input
	GOTO 	DELAY_5US			; SDA -> 1 and make 5us bus free time

; Generate start / repeated start condition on I2C bus
START:							; SDA 1 -> 0 while SCL == 1, then SCL -> 0
	BSF 	STATUS, RP0			; Bank 1 - Access TRISA
	CALL 	HIGH_SDA			; SDA 0 -> 1 ; wait 5 us - For repeated start
	CALL 	HIGH_SCL			; SCL 0 -> 1 ; make 5 us Start setup time
	CALL 	LOW_SDA				; SDA 1 -> 0 ; make 5 us Start hold  time
	GOTO 	LOW_SCL				; SCL 1 -> 0 ; wait 5 us

; Shift out a byte to I2C bus, clock in (n)ack, get data from digitindex
OUT_BYTE_ADDR
	MOVF	digitindex,w		; output address to be read

; Shift out a byte to I2C bus, clock in (n)ack, data is in w
OUT_BYTE:						; send o_byte on I2C bus
	movwf	BCD					; Store data to send
	MOVLW 	.8
	MOVWF 	FSR					; Loop for 8 bits
OUT_BIT:
	BTFSC 	BCD,7				; if  one, send a  one
	CALL 	HIGH_SDA			; SDA at logic one
	BTFSS 	BCD,7				; if zero, send a zero
	CALL 	LOW_SDA				; SDA at logic zero
	CALL 	CLOCK_PULSE			; SCL -> 1 ; 5 us wait, SCL -> 0 ; 5 us wait
	RLF 	BCD,F				; left shift, move mext bit to bit7
	DECFSZ 	FSR,F				; decrement bit counter - Leaves with FSR = 0
	GOTO 	OUT_BIT

; Clock in/out (n)ack
NACK:							; bring SDA high and clock, SDA must be input
	CALL 	HIGH_SDA
	CALL	CLOCK_PULSE			; SCL -> 1 ; 5 us wait, SCL -> 0 ; 5 us wait

; Make SDA low by making it output
LOW_SDA:						; SDA -> 0 ; 5 us wait
	BCF 	SDA					; make SDA pin an output
	GOTO 	DELAY_5US

; Generate clock pulse 			; SCL -> 1 ; 5 us wait, SCL -> 0 ; 5 us wait
CLOCK_PULSE:					; SCL momentarily to logic one
	CALL 	HIGH_SCL			; SCL -> 1 ; 5 us wait

; Make SCL low by making it output
LOW_SCL:						; SCL -> 0 ; 5 us wait
	BCF 	SCL					; make SCL pin an output
	GOTO	DELAY_5US

; Make SCL high by making it input
HIGH_SCL:						; SCL -> 1 ; wait 5 us
	BSF 	SCL					; make SCL pin an input

; Delay 5uS @ 20MHz
DELAY_5US:						; provides nominal >5 us delay @20MHz
								; 1 instuction takes 200ns
	MOVLW	 .8					; 0.2 us
	MOVWF 	dpi					; 0.2 us
DELAY_1:						; Loop of 3 inst. time
	DECFSZ 	dpi, F				; 7*0.2+0.4 us
	GOTO 	DELAY_1				; 7*0.4 us
	RETLW	RTC_ADDR			; Return address of RTC for OUT_BYTE 0.4 us

	endif

; Deepest Stack 				;These processors have an 8 level stack
;	1	Call	ProcessRC5
;	2	Call	StoreTime
;	3	Call	I2CByteWrite
;	4	Call	OUTBYTE
;	5	Call	CLOCK_PULSE
;	6	Call	HIGH_SCL
;	7	Interrupt routine
;	8	Call from interrupt routine

	END
