;************************************************************************
; 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 chips with 256 byte eeprom)				*
;   	 with adjustable speed											*
;  - Uses PCF8583-5 I2C RTC to store time.                         		*
;  - Settings (display mode, offset, scrolling spped ) stored in RTC	*
;                                                                 		*
;************************************************************************
;                                                               		*
;    Filename:	    prop628.asm                                        	*
;    StartDate:     02/12/2001                                        	*
;    LastUpdate:    09/09/2009											*
;    File Version:  2.27                                       	   		*
;                                                                     	*
; Base on the folowing version                                         	*
;    Author:        Soubry Henk                                       	*
;    Company:       Soubry Software Service                           	*
; Rewritten - extended                                                	*
;                                                                     	*
;************************************************************************
;                                                                    	*
;    Files required: CharGen628.asm                                     *
;                    Keys628.asm  	                                    *
;                                                                     	*
;************************************************************************
;                                                                    	*
;	Can be used on:                                                    	*
;					16F628, 16F628A, 16F648A, 16F819, 16F87, 16F88     	*
;                                                                    	*
;************************************************************************
;                                                                     	*
;    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								*
;                  RA6 can be freed up using ext 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		*
;                                                                     	*
;************************************************************************
	errorlevel	-302
#define	VersionMajor	0x02
#define	VersionMinor	0x27

	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	__16F819
		list	p=16f819			  ; 16F819 can be used
		#include <p16f819.inc>		  ; processor specific variable definitions
		#define	T0IE	TMR0IE
		#define	T0IF	TMR0IF
		#define	_MCLRE_OFF	_MCLR_OFF
EEPROM_SIZE	EQU	128
	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 "KeysURC1new.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		0xA000 | (VersionMajor << 8) | (VersionMinor)

;***** 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 used
#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 DISPLAYOFFSET	0x37	; Initial Display Offset for the hardware (display memory address) !!!


#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 RTC_ScrollSp	0x14	; Address of Scroll speed

;***** 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

	iFSR			; Display memory pointer of interrupt routine

; General flags
	flags			; Display flags
	flags2			; System  flags
; Context saving variable only for W, othes are on Bank2
	w_temp			; for w
  endc

	IF w_temp > 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	;

	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
	RotBCD10000		; 10000 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

	if EEPROM_SIZE==256
		cblock	0x110
					; More RAM space avaible on 16F87-88: 16
		endc
	endif

  cblock	0x120
					; More RAM space avaible: 48 for 16F628(A) and 80 for 16F684A ,16F87, 16F88
	status_temp		; for STATUS
	fsr_temp		; for FSR

	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
	ch_flags

 	; Not to use memory 0x170..0x17F  -  it will overwrite 0x070..0x07F
 endc

	ifdef __16F628
		IF ch_blanking >= 0x150
			ERROR "To many variables used in Bank2"
		ENDIF
	endif

	ifdef __16F628A
		IF ch_blanking >= 0x150
			ERROR "To many variables used in Bank2"
		ENDIF
	endif

	IF ch_blanking >= 0x170
		ERROR "To many variables used in Bank2"
	ENDIF

	if EEPROM_SIZE==256
		cblock	0x190
					; More RAM space avaible on 16F87-88: 96
		endc
	endif
	; 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

	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									; Reserved for backward compatibility
#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									; Not used

;******************************************************************************
;	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	"===> Ezt az ",0x88,"r",0x85,"t Kulcs",0x85,"r J",0x85,"nos ",0x86,"pitette  2009 EGER",0x83,0x84
    ifdef	__16F628
		de	" 16F628"
	endif
    ifdef	__16F628A
		de	" 16F628A"
	endif
	ifdef	__16F648A
		de	" 16F648A"
	endif
	ifdef	__16F819
		de	" 16F819"
	endif
	ifdef	__16F88
		de	" 16F88"
	endif
	ifdef	__16F87
		de	" 16F87"
	endif
		de	" V",VersionMajor+.48,".",((VersionMinor/.16)+.48),((VersionMinor%.16)+.48),"/RTC <==="
		de	0x00

;**********************************************************************
		ORG     0x0000			; 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     0x0004			; interrupt vector location
		movwf	w_temp			; Save W
		swapf	w_temp, f		; Prepare to restore with another swapf
		swapf	STATUS, w		; Get status
		clrf	STATUS
		bsf		STATUS,RP1		; Bank2
		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
		movf	RC5_flags,w
		andlw	(1<<7) | (1<<bRC5_DataReady)
		iorlw	(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
		bsf		STATUS,RP1		; Bank2
		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

;******************************************************************************
; 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 BCD     1 digit
		goto	l_BCD			;
	else
		movlw	6*(' '-' ')		; ' '
		goto	l_dot			;
	endif
;1--
	ifdef	RotationMeas
		movf	RotBCD,w		; Rot BCD    10 digit
		goto	l_BCD_swap		;
	else
		movlw	6*(' '-' ')		; ' '
		goto	l_dot			;
	endif
;2--
	ifdef	RotationMeas
		movf	RotBCD100,w		; Rot BCD   100 digit
		goto	l_BCD			;
	else
		movlw	6*(' '-' ')		; ' '
		goto	l_dot			;
	endif
;3--
	ifdef	RotationMeas
		movf	RotBCD100,w		; Rot BCD  1000 digit
		goto	l_BCD_swap		;
	else
		incf	WDay,w			; Day of week
		goto	l_convert		;
	endif
;4--
	ifdef	RotationMeas
		movf	RotBCD10000,w	; Rot BCD 10000 digit
		goto	l_BCD			;
	else
		movlw	6*(' '-' ')		; ' '
		goto	l_dot			;
	endif
;5--
		movlw	6*(' '-' ')		; ' '
		goto	l_dot			;
;6--
	ifdef	RotationMeas
		incf	WDay,w			; Day of week
		goto	l_convert		;
	else
		movlw	6*(' '-' ')		; ' '
		goto	l_dot			;
	endif
;7--
l_Space
		movlw	6*(' '-' ')		; ' '
		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	6*('-'-' ')		; '-'
		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	6*('-'-' ')		; '-'
		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	6*(':'-' ')		; ':'
		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	6*(':'-' ')		; ':'
		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_mult6							; Multiply by 6
		movwf	BCD				; BCD * 1
		addwf	BCD,f			; BCD = BCD * 2
		sublw	.9				; w = 9 - BCD
		btfss	STATUS,C		; Check for decimal character (A..F converted to space)
		goto	l_Space
		movf	BCD,w			; W = BCD * 2
		addwf	BCD,w			; W = BCD * 4
		addwf	BCD,w			; W = BCD * 6
		addlw	0x60			; Convert to character code, C=0
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
		xorlw	0x60			; Convert charcode to digit
		iorwf	digitindex,w	; Can only be 0 if digitindex = 0 and BCD value = 0x60
		btfsc	STATUS,Z		;
		movwf	BCD				; 10 hour digit is zero, remove leading zero using space
		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	.4				; Read the table backward
		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
		movlw	high(CharTab)	; Read character's dot pattern from CharTab
		movwf	PCLATH			; Set PCLATH
		movf	BCD,w			; Get index
		call    CharGen		   	; get the dot pattern for this column
		btfsc	dpi,6			; If on lower part
		goto	l_Date_3		;  flip dot pattern
		incf	BCD,f			; Move table pointer forward
		goto	OrToTdd			; Store data to tdd

l_Date_3
		movwf	Scratch2		; Flip the dot pattern
		clrf	Scratch			; Clear bit 8
		movlw	7				; Characters are 7 bit height
l_Date_4
		rrf		Scratch2,f		; Rotate (right) next bit to   C
		rlf		Scratch,f		; Rotate (left ) next bit from C
		addlw	-1				;
		btfss	STATUS,Z		; Loop for 7 dots
		goto	l_Date_4
		movf	Scratch,w		; Get flipped dot pattern
		decf	BCD,f			; Move table pointer backward
		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

		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	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	0xF0			; Set scrolling speed
		addwf	CCPR1H,w
		iorlw	0x0F			; Speeds are 0x0F, 0x1F,..., 0xEF, 0xFF
		movwf	CCPR1H
	ifdef	UsePCF8583RTC
		call	ScrollSpToRtc	; Save it to RTC
	endif
		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		;
;		goto	ProcessRC5Done

ProcessRC5Done
		movf	RC5_Cmd,w		; Store this command code to previous command
		movwf	RC5_Cmd2		;
		bcf		RC5_DataReady	; Data processed
SetOuterLed
		btfss	fDspEnabled		; Disable LEDs
		goto	OuterLedOff
	ifdef	UsePCF8583RTC
		bsf		CCPR1L,bOuterLED;
		btfss	ShowOuter		;
OuterLedOff
		bcf		CCPR1L,bOuterLED; Set OuterLED as in flags
		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
		btfsc	ShowOuter		;
		bsf		OuterLED		;
		btfss	ShowOuter		; Set OuterLED as in flags
OuterLedOff
		bcf		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

	ifdef	ExtendedDemo
		incf	flags,f			; Change display mode
	endif

; 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

		clrf	ch_flags
		btfsc	fDemo
		bsf		ch_flags,bDemo

	if	(EEADR & 0x180)!=(ch_blanking & 0x180)
		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

;******************************************************************************
;	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			; disable 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

	ifdef	__16F819
		movlw	0x06
		movwf	ADCON1			; On 16F819 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

	ifdef	CMCON
	  if	(CMCON & 0x180)==0x80
		bsf		STATUS,RP0		; Bank1 for 16F87 and 16F88, Voltage reference modul is off afther reset
	  endif	
		movwf	CMCON			; Turn comparators OFF
	  if	(CMCON & 0x180)==0x80
		bcf		STATUS,RP0		; Bank0
	  endif	
	endif

	ifdef	RotationMeas
		call	RotBlank		; Blanks rotation BCD data using flipped Spaces, returns Slowest scrolling speed
	endif
;		movwf	CCPR1H			; Init scrolling speed
		clrf	TMR1L
		clrf	TMR1H

		clrf	PixelWidth		;
		clrf	PixelOff		;

		clrf	flags2			; Clear system flags

	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	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

		clrf	TMR0			; restart timer

		movlw	(1<<T0IE) | (1<<INTE) | (1<<PEIE) | (1<<GIE)
		movwf	INTCON			; Enable interrupts, clear RBIF

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
									; None of these controller has EEADR on Bank3
	if	(EEADR & 0x080)==0x80
		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

									; None of these controller has EEADR on Bank3
	if	(EEADR & 0x180)!=(ch_blanking & 0x180)
		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

	if	(EEADR & 0x180)!=(ch_blanking & 0x180)
		banksel	ch_blanking			; Bank2
	endif

		btfss	ch_flags,bDemo		; 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	EECON1				; Select Bank of EEDATA, may be on Bank1 or Bank3

		bsf		EECON1, RD			; read character from EEProm

	if (EECON1 & 0x180)!=(EEADR & 0x180)
		bcf		STATUS,RP0			; Bank2 if EECON1 is on Bank3
	endif
									; If EECON1 is on Bank1 EEADR is there
		incf	EEADR,f				; inc address for next read

	if	EEPROM_SIZE==128
		bcf		EEADR,7				; if EEPROM_SIZE==128 roll over after 0x80 => 0x00
	endif

		movf	EEDATA,w			; Get next character

	if (EEDATA & 0x180)!=(ch_dot_index & 0x180)
		banksel	ch_dot_index		; Bank2 if EEADR is on Bank1
	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	RTC_Year		; 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	I2CByteRead		; Read Scroll speed
		iorlw	0x0F
		movwf	CCPR1H
								; digitindex = 0x15

		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
		call	TimeCheck		; Will correct 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

ScrollSpToRtc
		movlw	RTC_ScrollSp	; Address of scrolling speed in RTC
		movwf	digitindex
		movf	CCPR1H,w
		goto	I2CByteWrite	; Store it in RTC

	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		;
	ifdef	UsePCF8583RTC
		call	DispOffsToRtc
	endif
		movlw	0xFF			; Scroll speed
		movwf	CCPR1H
	ifdef	UsePCF8583RTC
		call	ScrollSpToRtc
	endif
		
		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

;******************************************************************************
; Writes the time 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 reset 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

	ifdef	RotationMeas
;******************************************************************************
;	Convert rotation counter to BCD
;******************************************************************************

ConvRot
		bcf		NewRot			; Clear new rotation data flag

		btfss	ShowRot			; If on dislplay
		goto	RotBlank

Word2Bcd5						; Max 65535 rot/minute

;Takes hex number in NumH:NumL  Returns decimal in ;TenK:Thou:Hund:Tens:Ones
;written by John Payson

;input

;=A3*16^3 + A2*16^2+ A1*16^1+ A0*16^0
;=A3*4096 + A2*256 + A1*16  + A0

NumH            EQU Rotation_H  ;A3*16+A2
NumL            EQU Rotation_L	;A1*16+A0

;=B4*10^4 + B3*10^3 + B2*10^2 + B1*10^1 + B0*10^0
;=B4*10000 + B3*1000 + B2*100 + B1*10 + B0

TenK            EQU RotBCD10000 ;B4
Thou            EQU Scratch2	;B3
Hund            EQU RotBCD100	;B2
Tens            EQU Scratch		;B1
Ones            EQU RotBCD		;B0

		swapf	NumH,w		;w  = A2*16+A3
        iorlw   0xF0		;w  = A3-16
        movwf   Thou		;B3 = A3-16
        addwf   Thou,f		;B3 = 2*(A3-16) = 2A3 - 32
        addlw   .226		;w  = A3-16 - 30 = A3-46
        movwf   Hund		;B2 = A3-46
        addlw   .50			;w  = A3-46 + 50 = A3+4
        movwf   Ones		;B0 = A3+4

        movf    NumH,w		;w  = A3*16+A2
        andlw   0x0F		;w  = A2
        addwf   Hund,f		;B2 = A3-46 + A2 = A3+A2-46
        addwf   Hund,f		;B2 = A3+A2-46  + A2 = A3+2A2-46
        addwf   Ones,f		;B0 = A3+4 + A2 = A3+A2+4
        addlw   .233		;w  = A2 - 23
        movwf   Tens		;B1 = A2-23
        addwf   Tens,f		;B1 = 2*(A2-23)
        addwf   Tens,f		;B1 = 3*(A2-23) = 3A2-69 (Doh! thanks NG)

        swapf   NumL,w		;w  = A0*16+A1
        andlw   0x0F		;w  = A1
        addwf   Tens,f		;B1 = 3A2-69 + A1 = 3A2+A1-69 range -69...-9
        addwf   Ones,f		;B0 = A3+A2+4 + A1 = A3+A2+A1+4 and Carry = 0 (thanks NG)

        rlf     Tens,f		;B1 = 2*(3A2+A1-69) + C = 6A2+2A1-138 and Carry is now 1 as tens register had to be negitive
        rlf     Ones,f		;B0 = 2*(A3+A2+A1+4) + C = 2A3+2A2+2A1+9 (+9 not +8 due to the carry from prev line, Thanks NG)
        comf    Ones,f		;B0 = ~(2A3+2A2+2A1+9) = -2A3-2A2-2A1-10 (ones complement plus 1 is twos complement. Thanks SD)

;;First two instructions make up negation. So,
;;Ones  = -1 * Ones - 1 
;;      = - 2 * (A3 + A2 + A1) - 9 - 1 
;;      = - 2 * (A3 + A2 + A1) - 10
        rlf     Ones,f	;B0 = 2*(-2A3-2A2-2A1-10) = -4A3-4A2-4A1-20

        movf    NumL,w		;w  = A1*16+A0
        andlw   0x0F		;w  = A0
        addwf   Ones,f		;B0 = -4A3-4A2-4A1-20 + A0 = A0-4(A3+A2+A1)-20 range -215...-5 Carry=0
        rlf     Thou,f		;B3 = 2*(2A3 - 32) = 4A3 - 64

        movlw   0x07		;w  = 7
        movwf   TenK		;B4 = 7

;B0 = A0-4(A3+A2+A1)-20	;-5...-200
;B1 = 6A2+2A1-138	;-18...-138
;B2 = A3+2A2-46		;-1...-46
;B3 = 4A3-64		;-4...-64
;B4 = 7			;7
; At this point, the original number is
; equal to TenK*10000+Thou*1000+Hund*100+Tens*10+Ones 
; if those entities are regarded as two's compliment 
; binary.  To be precise, all of them are negative 
; except TenK.  Now the number needs to be normal- 
; ized, but this can all be done with simple byte 
; arithmetic.

        movlw   .10			; w  = 10
Lb1:			;do
        addwf   Ones,f		; B0 += 10
        decf    Tens,f		; B1 -= 1
        btfss   STATUS,C
         goto   Lb1			; while B0 < 0
Lb2:			;do
        addwf   Tens,f		; B1 += 10
        decf    Hund,f		; B2 -= 1
        btfss   STATUS,C
         goto   Lb2			; while B1 < 0
Lb3:			;do
        addwf   Hund,f		; B2 += 10
        decf    Thou,f		; B3 -= 1
        btfss   STATUS,C
         goto   Lb3			; while B2 < 0
Lb4:			;do
        addwf   Thou,f		; B3 += 10
        decf    TenK,f		; B4 -= 1
        btfss   STATUS,C
         goto   Lb4			; while B3 < 0

		movf	RotBCD10000,w	; Remove leading zeros, pack 5 BCD digit into 3 byte
		movwf	FSR
		btfsc	STATUS,Z
		iorlw	0x0F
		movwf	RotBCD10000
		swapf	Scratch2,w
		iorwf	FSR,f
		btfsc	STATUS,Z
		iorlw	0xF0
		iorwf	RotBCD100,f
		movf	RotBCD100,w
		andlw	0x0F
		iorwf	FSR,f
		btfsc	STATUS,Z
		iorlw	0x0F
		iorwf	RotBCD100,f
		swapf	Scratch,w
		iorwf	FSR,f
		btfsc	STATUS,Z
		iorlw	0xF0
		iorwf	RotBCD,f
        return

; Blank rotation BCD data using Space character
RotBlank
		movlw	0xFF			; Blank the rotation dislpay
		movwf	RotBCD10000
		movwf	RotBCD100
		movwf	RotBCD			; Store r/m unit
		return
	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
