	list b=4
;************************************************************************
; 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 and Day of week     					*
;  - 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		*
;                                                                 		*
;************************************************************************
;                                                               		*
;    Filename:	    prop.asm                                        	*
;    StartDate:     02/12/2001                                        	*
;	 LastUpdate:    06/10/2018											*
#define	VersionMajor	0x02
#define	VersionMinor	0x20
;                                                                     	*
;    Original 16F628 version by:                                       	*
;    Author:        Soubry Henk                                       	*
;    Company:       Soubry Software Service                           	*
;                                                                     	*
;    Rewritten, extended:                                              	*
;    Ported to 16F648A	16F88											*
;	 Displays day of week, century										*
;    Name of weekdays on 16 languages									*
;	 Selectable date formats ((CC)YY-MM-DD / DD-MM-(CC)YY)				*
;	 Rotation speed measurement											*
;	 Two scrolling message with adjustable speed						*
;	 Static text display												*
;    12/24 hour digital/analog display mode								*
;	 All digital display can be done in Binary Coded Decimal			*
;	 Individualy controllable analog hands								*
;	 Dot/arc second hand, bar/dot minute hand option					*
;	 Doubble/single tick at 12'o clock									*
;	 Store time and settings in a PCF8583 I2C RTC						*
;	 Calibration pin can be used to select rotation direction			*
;    Vertical LED arrangement option, bottomn of display not mirrored	*
;    Vertical LED display rotates at Scrollspeed						*
;                                                                     	*
;************************************************************************
;                                                                    	*
;    Files required: CharGen648.asm                                    	*
;                    Keys648.asm                                      	*
;                    DayNames648.asm                                   	*
;                                                                     	*
;************************************************************************
;                                                                     	*
;    Notes:                                                           	*
;    	Pin assignment                                                	*
;    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	  								*
;                                                                     	*
;************************************************************************
;																		*
;  RTC Ram layout														*
;	00		01		02		03		04		05		06		07			*
;	cmd		1/10	sec		minute	hour	day		month	Timer		*
;			1/100							year1.0	wday	10Day/Day	*
;			sec												not used	*
;																		*
;	08		09		0A		0B		0C		0D		0E		0F			*
;	A	L	A	R	M		R	E	G	I	S	T	E	R	S			*
;						not used										*
;																		*
;	10		11		12		13		14		15		16		17			*
;	Year	~Year	Century	Flags	Disp	Scroll	Flags3	Flags4		*
;									offs	speed						*
;																		*
;	18		19		1A		1B		1C		1D		1E		1F			*
;	Lang.	Flags5														*
;																		*
;************************************************************************
;	Ir Remote control keys												*
;																		*
;	See keys.asm and keys.txt											*
;																		*
; Afther time setting Toggle Outer Led command stores time to RTC		*
;************************************************************************

	errorlevel	-302

	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

	#include "CharGen648.asm"
	#include "Keys648.asm"
	#include "DayNames648.asm"

	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
		__CONFIG _CP_OFF & _WDT_OFF & _BODEN_ON & _PWRTE_ON & _HS_OSC & _MCLRE_OFF & _LVP_OFF
	endif


	__idlocs		0xA000 | (VersionMajor << 8) | (VersionMinor)

RTC_ADDR	EQU	0xA0			; Address of RTC

;***** VARIABLE DEFINITIONS

#define	VerticalLEDs			; un-comment this line if leds are arranged vertical to rotation RB7 on top 
#define RB6OnTop

;#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 MotorDirFromCalibration	; un-comment this line if get motor direction from calibration pin
;#define ReversTextScroll		; un-comment this line if your text must scroll from left to right (e.g. Hebrew)
;#define SlashedZero			; comment this line to use normal zero on time/date display
#define RC5AddrCheck			; comment this line if RC5 Address checking not required
;#define UsePCF8583RTC			; comment this line if No PCF8583 RTC used

#define DISPLAYOFFSET	0x37	; Initial Display Offset for the hardware (display memory address) !!!

;#define	RTC_Control	0x00	; Control register
#define RTC_Tenms		0x01	; Address of 1/100 Seconds
#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_century		0x12	; Address of century
#define	RTC_flags		0x13	; Address of flags
#define	RTC_disp_off	0x14	; Address of DispOffset
#define RTC_ScrollSp	0x15	; Address of Scroll speed
#define	RTC_flags3		0x16	; Address of flags3
#define	RTC_flags4		0x17	; Address of flags4
#define	RTC_Language	0x18	; Address of Language
#define	RTC_flags5		0x19	; Address of flags5


;Display memory		Low byte
;DispI			    0x020		; 2*40 bytes from 0x20 -> 0x48
;DispII			    0x0A0		; 2*80 bytes from 0xA0 -> 0xF0

; Common memory 0x070-0x07F, 0x0F0-0x0FF, 0x170-0x17F, 0x1F0-0x1FF

;Vars in shared memory. This memory is available from any bank
  cblock	0x70
	Scratch						; memory locations for general use
	Scratch2					;
	Scratch3					;
	Scratch4					;

;Vars for building display-content
	BCD							; Binary coded decimal
	BCD_High					;   high part
	digitindex					; index digit to display
	dotindex					; index dot to display
	dpi							; 0..119 display index for main program
	tdd							; temporary display data
	I2CWordAddr					;

	flags						; Digital  display flags - more bits in flags5:5..4
	flags2						; System  flags
	flags3						; Analogue display flags - more bits in CCP1CON:5..4

	iFSR						; copy of FSR used by interrupt routine
	w_temp						; variable used for context saving
  endc
	IF w_temp >= 0x80
		ERROR "To many variables used in Common Bank"
	ENDIF

;Vars for display timing routines and interrupts
  cblock	0x20
    DispI: .40					; Display data 0..39
	PeriodCnt_H					; 16 bit counter counts number of interrupt
	PeriodCnt_L					; between index-pulses (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
	NewPixelWidth				; New pixel width calculated by main programm

	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
	Century						; 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 : Monday is 1

	Language
	flags5

;Vars for RC5 decoding
	RC5_flags					;
	RC5_Tmr						;
	RC5_Addr					;
	RC5_Cmd						;
	RC5_Cmd2					; storage for previous cmd
  endc

	IF RC5_Cmd2 >= 0x70
		ERROR "To many variables used in Bank 0"
	ENDIF

; start a new block of vars in bank1
  cblock	0xA0
	DispII:	.80					; Display data 40..119
  endc

; start a new block of vars in bank2

  cblock	0x120				; Bank 2 - 16F648A 80 bytes

	ch_dot_point_H				; pointer the the current dot# in a character
	ch_dot_point_L				; points the the chargenerator table
	ch_dot_index				; index of dot# in a char, 0..5
	ch_flags					; char flags
	ch_blanking					; counter for blanking the display when scrolling text

	rotation_L					; Rotation count
	rotation_H

	rot_bcd:5					; Rotation in BCD 1/min

	Year16L						; A 16 bit register Year
	Year16H

	Scratch5
	Scratch6

	pclath_temp					; variable used for context saving
	fsr_temp					; variable used for context saving
	status_temp					; variable used for context saving
  endc
	IF status_temp >= 0x160
		ERROR "To many variables used in Bank 2"
	ENDIF


;**********************************************************************
; give meaningfull names to the scratch locations

; for the 16bit/8bit divide routine
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
; Port A
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
bHourLED		EQU	3	; Hour   hand's LED
bMinLED			EQU	4	; Minute hand's LED
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
bMotorDirCC		EQU	1	; Direction of motor rotation  -  Read at reset from PORTC,bCalibration
bRC5_TimeSet 	EQU 2	; Time setting commands were received
bNewRot			EQU	4	; New rotation count available
bPStorage		EQU	5	; Rotation time measurement buffer select

; CCP1CON				; More analog  display flags
bSTick12		EQU	4	; Single / double tick at 12'o clock
;				EQU	5

; flags5				; More digital display flags
bBinMode		EQU	4	; Binary display mode
;				EQU	5

; CCPR1L				; Stores a copy of PORTC - needed only for sw I2C
; CCPR1H				; Stores Scrolling speed

; define flag bits		; Digital Display flags
bShowOuter		EQU	0	; Show / hide outer ring
bShowDayName	EQU	1	; Show   day's name instead day of week
bDisp12Mode		EQU	2	; 12   / 24 hour mode
bDateFormat		EQU	3	; Use YYYY-MM-DD / DD-MM-YYYY or YY-MM-DD / DD-MM-YY date format
bShowWDay		EQU	4	; Show / hide day of week - Show always date
bShowDTime		EQU	5	; Show / hide digital time
bShowDDate		EQU	6	; Show / hide digital date
bShowCentury	EQU	7	; Show / hide century

; define flag3 bits		; Analog Display flags
bShow36912		EQU	0	; Show / hide drawing III, VI, IX, XII around the clock
bShowTicks		EQU	1	; Show / hide ticks
bSecMode		EQU	2	; Dot  / Arc second hand
bMinMode		EQU	3	; Dot  / Bar minute hand
bShowSecHand	EQU	4	; Show / hide second hand
bShowMinHand	EQU	5	; Show / hide minute hand
bShowHourHand	EQU	6	; Show / hide hour hand
bAnalMode24h	EQU	7	; 24   / 12 hour mode


; define flag2 bits		; System flags
bDspEnabled		EQU	0	; Blank the display - motor slow or not stabilized
bNewPCnt		EQU	1	; New pitch calculated
bNewTime		EQU	2	; New time calculated
bNewPPitch		EQU	3	; New pitch measured
bText			EQU	4	; Text on display
bScrollOn		EQU	5	; Scrolling text on display
bDemo			EQU	6	; Demo mode active
bTimeInv		EQU	7	; Time invalid

; 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


; 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

; T1CON
#define		MotorDirCC		T1CON,bMotorDirCC
#define		NewRot			T1CON,bNewRot
#define		PStorage		T1CON,bPStorage
#define		RC5_TimeSet		T1CON,bRC5_TimeSet

; TMR1 		registers used for rotation metering

; CCP1CON	flags4
#define		STick12			CCP1CON,bSTick12

; flags5
#define		BinMode			flags5,bBinMode

; CCPR1H 	register  used to store scroll speed

; Flags
#define		ShowOuter		flags,bShowOuter
#define		ShowDayName		flags,bShowDayName
#define		Digit12Mode		flags,bDisp12Mode
#define		DateFormat		flags,bDateFormat
#define		ShowWDay		flags,bShowWDay
#define		ShowDTime		flags,bShowDTime
#define		ShowDDate		flags,bShowDDate
#define		ShowCentury		flags,bShowCentury

; Flags3
#define		Show36912		flags3,bShow36912
#define		ShowTicks		flags3,bShowTicks
#define		SecMode			flags3,bSecMode
#define		MinMode			flags3,bMinMode
#define		ShowSecHand		flags3,bShowSecHand
#define		ShowMinHand		flags3,bShowMinHand
#define		ShowHourHand	flags3,bShowHourHand
#define		AnalMode24h		flags3,bAnalMode24h

; 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
#define		RC5_HalfBit		RC5_flags,bRC5_HalfBit
#define		RC5_Idle		RC5_flags,bRC5_Idle
#define		RC5_prev_inp	RC5_flags,bRC5_prev_inp
#define		RC5_ReSynced	RC5_flags,bRC5_ReSynced

;******************************************************************************
;	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 initial message
;******************************************************************************

	ORG     0x2170           	; Start of EEPROM message
;       	 0123456789ABCDE   F
		de	"Propeller Clock", 0x00

	ORG     0x2100           	; Start of EEPROM page 0
		de	"===> This version of Bob Blick's Propeller Clock was built with "
	ifdef	__16F648A
		de	" 16F648A"
	endif
	ifdef	__16F87
		de	" 16F87"
	endif
	ifdef	__16F88
		de	" 16F88"
	endif
	ifdef UsePCF8583RTC
		de	", PCF8583 RTC"
	endif
		de	" ",0x83,0x84
		de	" Ver. ",VersionMajor | 0x30 ,".", ((VersionMinor>>4) & 0x0F) | 0x30, ((VersionMinor) & 0x0F) | 0x30, " <==="
		de	0x00

	ORG     0x2180           	; Start of EEPROM page 1
		de	"===> Displays day of week, rotation speed"
	ifdef UsePCF8583RTC
		de	" and store settings and time in PCF8583 RTC "
	endif
		de	". 06/10/2018 "
		de	,0x83,0x84," <===",0x00

;**********************************************************************
		ORG     0x000           ; processor reset vector
		clrf	PORTA
		clrf	PORTB
		clrf	CCPR1L
		goto    main            ; go to beginning of program

;**********************************************************************
		ORG     0x0004			; interrupt vector location
		movwf	w_temp			; context saveing
		swapf	w_temp, f
		swapf	STATUS, w		; Save STATUS
		bcf		STATUS,RP0
		bsf		STATUS,RP1		; Context saving variables are on Bank2
		movwf	status_temp
		swapf	PCLATH,w		; Save PCLATH
		movwf	pclath_temp		; Needed only if >2K program memory used
		movf	FSR,w			; context saving
		movwf	fsr_temp		;

		clrf	PCLATH			; IT routine is in 0x000..0x7FF
		clrf	STATUS			; select bank 0 & irp 0 for interrupt stuff

;--------
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
;		movwf	PixelPitch_H	; High part
		movf	NewPPitch_L,w	; PixelPitch = NewPPitch
		movwf	PixelPitch_L	; Low  part
		movf	NewPPitch_F,w	;
		movwf	PixelPitch_F	; Fraction part
		movf	NewPixelWidth,w
		movwf	PixelWidth
		bcf		NewPPitch		; Clear new value calculated flag

lINT_RB0
		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

		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	MotorDirFromCalibration
		btfsc	MotorDirCC		; Get direction of motor rotation
		call	CheckDecrementFSR; decrement FSR, check correct progress of diplay memory pointer
		btfss	MotorDirCC		; Get direction of motor rotation
		call	CheckIncrementFSR; increment FSR, check correct progress of diplay memory pointer
	else

	  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
	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
		bcf		PIR1,TMR2IF		; clear TMR2 interrupt flag

								; 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
		movlw	.120			; Minute passed
		xorwf	Second2,w
		btfss	STATUS,Z
		goto	lTime_1			; No - do RC5 task

		movf	TMR1L,w			; Read rotation from Timer1
		bsf		STATUS,RP1
		movwf	rotation_L		; Store it
		bcf		STATUS,RP1
		movf	TMR1H,w
		bsf		STATUS,RP1
		movwf	rotation_H
		bcf		STATUS,RP1

		clrf	TMR1H			; Reset Timer 1
		clrf	TMR1L

		bsf		NewRot			; Sign new rotation data available

;--------
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
		clrf 	RC5_Addr		; Clear address and command
		movlw	0x04
		movwf	RC5_Cmd			; 13 rlf shifts this bit to bit 7 of RC5_Addr
		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
		btfss	RC5_Addr,7
		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

;--------
INT_EXIT
		bsf		STATUS,RP1		; Context saving variables are on Bank2 except w_temp
		movf	fsr_temp,w		; context recall
		movwf	FSR				;
		swapf	pclath_temp,w	; Needed only if >2K program memory used
		movwf	PCLATH
		swapf	status_temp, w	; restore context for main program
		movwf	STATUS			;  this will also restore bank selection
		swapf	w_temp, w
		retfie                  ; return from interrupt

;******************************************************************************
;	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		;
		return

;**********************************************************************

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	NewPixelWidth	; W = NewPixelWidth = NewPPitch_L * 4 / 8
		clrc
		rrf		NewPixelWidth,f	; NewPixelWidth = NewPPitch_L * 2 / 8
		clrc
		rrf		NewPixelWidth,f	; NewPixelWidth = NewPPitch_L * 1 / 8
		addwf	NewPixelWidth,f	; NewPixelWidth = NewPPitch_L * 5 / 8

		btfss	flags2,bTimeInv
		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


;******************************************************************************
;	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	lNotFeb			; continue
		decf	DMon,f			; February has max 29 days

		movf	Year,w
		btfsc	STATUS,Z		; Year == 0 check Century instead
		movf	Century,w
		andlw	0x03
		btfss	STATUS,Z
		decf	DMon,f			; Year is not a LeapYear, dec DMon for Feb

lNotFeb
		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			;
		call	CheckAndIncNext
								; FSR -> Century
		movlw	.100			;
		subwf	Century,w		; Century < 100 ?
		btfsc	STATUS,C		;
		clrf	Century			;

		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 name of day

LoadBCDDay
		movf	digitindex,w	; Read names backward
		sublw	.9
		movwf	Scratch
		swapf	Language,w		; Multiply language code by 7
		movwf	BCD
		clrc
		rrf		BCD,f
		movf	Language,w
		subwf	BCD,w
		addwf	WDay,w			; Add day of week
		movwf	BCD				; Multiply by 10
		addwf	BCD,f
		movf	BCD,w
		rlf		BCD,f
		rlf		BCD_High,f
		rlf		BCD,f
		rlf		BCD_High,f
		addwf	BCD,w
		btfsc	STATUS,C
		incf	BCD_High,f
		addwf	Scratch,w		; Add 9-digitindex
		btfsc	STATUS,C
		incf	BCD_High,f
		movwf	BCD
		movlw	high(DayNames)	; Add address of table
		call	CharPattern		; Get next character
		clrf	PCLATH			; Use page 0

		clrf	BCD_High
		addlw	-' '			; Convert ascii code to internal
		clrc
		movwf	BCD
		bcf		BCD,7
		rlf		BCD,f			; Multiply by 6
		rlf		BCD,w
		rlf		BCD_High,f
AddBCD16
		addwf	BCD,f
		btfsc	STATUS,C
		incf	BCD_High,f
		return					; Store new code

CharPatternTab
		btfsc	BCD_High,7
		goto	CharPatternBin
		movlw	high(CharTab)
CharPattern						; If calls out from lower 2K, afther call PCLATH has to be set to 0
		addwf	BCD_High,w		; Get high byte
GetXII
		movwf	PCLATH			; Store it in PCLATH
		movf	BCD,w			; Get low byte
		movwf	PCL				; Get next character

;=======================================================
; No execution passes here

CharPatternBin					; If calls out from lower 2K, afther call PCLATH has to be set to 0
		movlw	high(BinTab)	; Get high byte
		movwf	PCLATH			; Store it in PCLATH , PCLATH has only 5 bits - bit7 of BCD removed
		movf	BCD,w			; Get low byte
		addlw	low(BinTab)-(6*('0'-' ')) ; Convert start address of character
		movwf	PCL				; Get next character

;-------- Update Display memory with Digital Time Display

DTime
		movf	dpi,f			;
		btfss	STATUS,Z		;
		goto	l_DTime_1		; display index > 0

		clrf	digitindex		; display index = 0, load start parameters for
		clrf	dotindex		; digit displaying

l_DTime_1
		movlw	.60				;
		subwf	dpi,w			;
		btfsc	STATUS,C		;
		return					; display index >= 60

		movf	dotindex,w		;
		btfss	STATUS,Z		;
		goto	l_DTime_3

		movlw	.6
		addwf	dotindex,f
		clrf	BCD
		clrf	BCD_High

		btfsc	ShowDTime
		call	LoadBCDTime		; dotindex = 0, load new digit

		btfss	ShowDTime
		call	LoadBCDTest		; dotindex = 0, load new digit

l_DTime_3
	#ifdef	VerticalLEDs
		movlw	.4				; Move index of character table
		call	AddBCD16
	#endif

		decfsz	dotindex,f		;
		goto	l_DTime_2

		incf	digitindex,f	; select next digit
		return					; to get a gap between digits

l_DTime_2
		call	CharPatternTab	; get the dot pattern for this column
		clrf	PCLATH

IorToTdd
	#ifdef	VerticalLEDs
		goto	l_Date_3
	#endif
		iorwf	tdd,f			; Load pixed data to temp disp data

IncBCD16
		movlw	1				; Move index of character table
		goto	AddBCD16

;-------- Update Display memory with Digital Date Display

DDate
		movlw	.60
		subwf	dpi,w			;
		btfss	STATUS,C		;
		return					; display index < 60
		btfss	STATUS,Z		;
		goto	l_DDate_1		; display index > 60

		clrf	digitindex		; display index = 60, load start parameters for
		clrf	dotindex		; digit displaying

l_DDate_1
		movf	dotindex,w		;
		btfss	STATUS,Z		;
		goto	l_DDate_11

		movlw	.6
		addwf	dotindex,f
		clrf	BCD
		clrf	BCD_High

		btfss	ShowDDate
		goto	l_DDate_6

		btfss	ShowWDay
		goto	l_DDate_9

		btfss	Second2,3
		call	LoadBCDWDay
		btfsc	Second2,3
l_DDate_9
		call	LoadBCDDate		; dotindex = 0, load new digit

l_DDate_10
		movlw	.4				; Read dot patterns backward
		call	AddBCD16

l_DDate_11
		decfsz	dotindex,f		;
		goto	l_DDate_2		; dotindex < 6, display data

		incf	digitindex,f	; select next digit
		return					; to get a gap between digits

l_DDate_6
		btfsc	ShowWDay
		call	LoadBCDWDay
		goto	l_DDate_10

l_DDate_2
		call	CharPatternTab	; get the dot pattern for this column
		clrf	PCLATH

l_Date_3
		call	FlipPattern
		iorwf	tdd,f			; Load pixed data to temp disp data

DecBCD16
		decf	BCD,f			; Move table pointer backward
		incf	BCD,w
		btfsc	STATUS,Z
		decf	BCD_High,f
		return					;

FlipPattern
		movwf	Scratch2		; Flip the dot pattern
		clrf	Scratch			; Clear bit 7
		movlw	7				; Characters are 7 bit height
l_FlipPattern_1
		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_FlipPattern_1
		movf	Scratch,w		; Get flipped dot pattern
		return

;--------
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

;-------- 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		;

		btfss	ShowSecHand		; Second hand disabled
		goto	MinuteHand

		movf	Second2,w		;
		andlw	0xFE			; filter out 1/2 second

		subwf	Scratch4,w
ArcSec							; Draw arc if Scratch4<= (Second2 and 0xFE)
		btfss	SecMode			; Set C if dot second hand mode
		bsf		STATUS,C
		btfss	STATUS,C
		bsf		tdd,bSecLED		; Sec led - second hand
SecHand
		btfsc	STATUS,Z		;
		bsf		tdd,bSecLED		; Sec led - second hand

MinuteHand
		bcf		STATUS,C		;
		rlf		Minute,w		; Clears C, because Minute < .60
		movwf	Scratch			; Scratch = minute * 2

		btfss	ShowMinHand		; Minute hand disabled
		goto	HourHand

		xorwf	Scratch4,w		;
		btfss	STATUS,Z		;
		goto	HourHand		; Minute hand not in this dpi

		btfsc	MinMode			;
		goto	DotMinHand

		movlw	B'00001111'		;
		iorwf	tdd,f			; Turn on minute hand
		bsf		tdd,bInnerLED	; Turn on analog innner Leds
		goto	HourHand

DotMinHand
		bsf		tdd,bMinLED		; Turn on minute dot

HourHand
		btfss	ShowHourHand	; Hour hand disabled
		return

		rlf		Hour,w			; Get hour
		movwf	Scratch2		; 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					; Divide (2 * minute) by 12
		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)

		btfsc	AnalMode24h
		rrf		Scratch2,f

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		;
		btfsc	STATUS,Z		; Turn on hour hand
		bsf		tdd,bInnerLED	; Turn on analog innner Leds
		return	

;-------- Get character Digital Date Display
; For characters ' ' to 'J'    Set BCD_High = 0
; For characters 'K' to 'u'    Set BCD_High = 1
; For characters 'v' to 0x9F   Set BCD_High = 2
;
;--------
LoadBCDDate						; load BCD with date digits
		movlw	high(DateTable0)
		movwf	PCLATH
		movf	digitindex,w	;
		addwf	digitindex,w	;
		btfss	ShowCentury
		addlw	DF_YYMMDD-DF_YYYYMMDD
		btfsc	DateFormat
		addlw	DF_DDMMYYYY-DF_YYYYMMDD

DateTable0
		addwf	PCL,f			;

; DateFormat: YYYY-MM-DD
DF_YYYYMMDD
;0--
		movf	Day,w			; Day     1 digit
		goto	l_convert		;
;1--
		movf	Day,w			; Day    10 digit
		goto	l_convert_swap	;
;2--
		movlw	6*('-'-' ')		; '-'
		goto	l_dot			;
;3--
		movf	Month,w			; Month   1 digit
		goto	l_convert		;
;4--
		movf	Month,w			; Month  10 digit
		goto	l_convert_swap	;
;5--
		movlw	6*('-'-' ')		; '-'
		goto	l_dot			;
;6--
		movf	Year,w			; Year    1 digit
		goto	l_convert		;
;7--
		movf	Year,w			; Year   10 digit
		goto	l_convert_swap
;8--
		movf	Century,w		; Year  100 digit
		goto	l_convert		;
;9--
		movf	Century,w		; Year 1000 digit
		goto	l_convert_swap

;  DateFormat: DD-MM-YYYY
DF_DDMMYYYY
;10--
		movf	Year,w			; Year    1 digit
		goto	l_convert		;
;11--
		movf	Year,w			; Year   10 digit
		goto	l_convert_swap	;
;12--
		movf	Century,w		; Year  100 digit
		goto	l_convert		;
;13--
		movf	Century,w		; Year 1000 digit
		goto	l_convert_swap	;
;14--
		movlw	6*('-'-' ')		; '-'
		goto	l_dot			;
;15--
		movf	Month,w			; Month   1 digit
		goto	l_convert		;
;16--
		movf	Month,w			; Month  10 digit
		goto	l_convert_swap	;
;17--
		movlw	6*('-'-' ')		; '-'
		goto	l_dot			;
;18--
		movf	Day,w			; Day     1 digit
		goto	l_convert		;
;19--
		movf	Day,w			; Day    10 digit
		goto	l_convert_swap

; DateFormat: YY-MM-DD
DF_YYMMDD
;20--
		movlw	6*(' '-' ')		; ' '
		goto	l_dot			;
;21--
		movf	Day,w			; Day     1 digit
		goto	l_convert		;
;22--
		movf	Day,w			; Day    10 digit
		goto	l_convert_swap	;
;23--
		movlw	6*('-'-' ')		; '-'
		goto	l_dot			;
;24--
		movf	Month,w			; Month   1 digit
		goto	l_convert		;
;25--
		movf	Month,w			; Month  10 digit
		goto	l_convert_swap	;
;26--
		movlw	6*('-'-' ')		; '-'
		goto	l_dot			;
;27--
		movf	Year,w			; Year    1 digit
		goto	l_convert		;
;28--
		movf	Year,w			; Year   10 digit
		goto	l_convert_swap
;29--
		movlw	6*(' '-' ')		; ' '
		goto	l_dot			;

;  DateFormat: DD-MM-YY
DF_DDMMYY
;30--
		movlw	6*(' '-' ')		; ' '
		goto	l_dot			;
;31--
		movf	Year,w			; Year    1 digit
		goto	l_convert		;
;32--
		movf	Year,w			; Year   10 digit
		goto	l_convert_swap	;
;33--
		movlw	6*('-'-' ')		; '-'
		goto	l_dot			;
;34--
		movf	Month,w			; Month   1 digit
		goto	l_convert		;
;35--
		movf	Month,w			; Month  10 digit
		goto	l_convert_swap	;
;36--
		movlw	6*('-'-' ')		; '-'
		goto	l_dot			;
;37--
		movf	Day,w			; Day     1 digit
		goto	l_convert		;
;38--
		movf	Day,w			; Day    10 digit
		goto	l_convert_swap
;39--
		goto	l_Space			;
DateTable1

	IF high(DateTable0) != high(DateTable1)
		ERROR "Date jump table page error"
        ENDIF

LoadBCDTime						; load BCD with time digits
		movf	Hour,w
		btfss	Digit12Mode		; 24 hour mode
		goto	lBCDHour
		addlw	-.12			; Prepare to 12 hour mode
		btfss	STATUS,C		; If hour > .12: add -.12
		movf	Hour,w			; Reload hour if hour
		btfsc	STATUS,Z
		addlw	.12				; Correct 0 to 12
lBCDHour
		movwf	BCD

		movlw	high(TimeTable0)
		movwf	PCLATH
		movf	digitindex,w	;
		addwf	digitindex,w	; Clears C

TimeTable0
		addwf	PCL,f			;
	#ifdef	VerticalLEDs
;0--
		nop
		goto	l_Space			;
;1--
		rrf		Second2,w		; Second  1 digit
		goto	l_convert		;
;2--
		rrf		Second2,w		; Second 10 digit
		goto	l_convert_swap	;
;3--
		movlw	6*(':'-' ')		; ':'
		goto	l_dot			;
;4--
		movf	Minute,w		; Minute  1 digit
		goto	l_convert		;
;5--
		movf	Minute,w		; Minute 10 digit
		goto	l_convert_swap	;
;6--
		movlw	6*(':'-' ')		; ':'
		goto	l_dot			;
;7--
		movf	BCD,w			; Hour    1 digit
		goto	l_convert		;
;8--
		movf	BCD,w			; Hour   10 digit
		goto	l_leading_swap_PM;
;9--
		movf	BCD,w
		goto	l_PM			;
	#else
;0--
		movf	BCD,w
		goto	l_PM			;
;1--
		movf	BCD,w			; Hour   10 digit
		goto	l_leading_swap_PM;
;2--
		movf	BCD,w			; Hour    1 digit
		goto	l_convert		;
;3--
		movlw	6*(':'-' ')		; ':'
		goto	l_dot			;
;4--
		movf	Minute,w		; Minute 10 digit
		goto	l_convert_swap	;
;5--
		movf	Minute,w		; Minute  1 digit
		goto	l_convert		;
;6--
		movlw	6*(':'-' ')		; ':'
		goto	l_dot			;
;7--
		rrf		Second2,w		; Second 10 digit
		goto	l_convert_swap	;
;8--
		rrf		Second2,w		; Second  1 digit
		goto	l_convert		;
;9--
		goto	l_Space			;
	#endif
TimeTable1

	IF high(TimeTable0) != high(TimeTable1)
		ERROR "Time jump table page error"
        ENDIF

LoadBCDWDay
		btfsc	ShowDayName
		goto	LoadBCDDay

		movlw	high(TempTable0)
		movwf	PCLATH
		movf	digitindex,w	;
		addwf	digitindex,w	; Clears C

TempTable0
		addwf	PCL,f			;
;0--
l_Space
		movlw	6*(' '-' ')		; ' '
		goto	l_dot_clr		;
;1--
		movlw	6*(' '-' ')		; ' '
		goto	l_dot_clr		;
;2--
		movlw	6*(' '-' ')		; ' '
		goto	l_dot_clr		;
;3--
		movlw	6*(' '-' ')		; ' '
		goto	l_dot_clr		;
;4--
		movlw	6*(' '-' ')		; ' '
		goto	l_dot_clr		;
;5--
		incf	WDay,w			; Day of week
		goto	l_convert		;
;6--
		movlw	6*(' '-' ')		; ' '
		goto	l_dot_clr		;
;7--
		movlw	6*(' '-' ')		; ' '
		goto	l_dot_clr		;
;8--
		movlw	6*(' '-' ')		; ' '
		goto	l_dot_clr		;
;9--
		goto	l_Space			;
TempTable1

	IF high(TempTable0) != high(TempTable1)
		ERROR "Temp jump table page error"
        ENDIF

;-------- Update Display memory with rotation measurement

LoadBCDTest						; load BCD with test digits
		movlw	high(TestTable0)
		movwf	PCLATH
		movf	digitindex,w	;
		addwf	digitindex,w	; Clears C
		bsf		STATUS,RP1		; Bank2

TestTable0
		addwf	PCL,f			;
	#ifdef	VerticalLEDs
;0--
		movlw	6*(' '-' ')		; ' '
		goto	l_dot			;
;1--
		movlw	low(6*('m'-' ')); 'm'
		goto	l_dot_BCDH_1	;
;2--
		movlw	6*('/'-' ')		; '/'
		goto	l_dot			;
;3--
		movlw	low(6*('R'-' ')); 'R'
		goto	l_dot_BCDH_1	;
;4--
		movf	rot_bcd,w		; Rotation digit     1
		goto	l_BCD			;
;5--
		movf	rot_bcd+1,w		; Rotation digit    10
		goto	l_BCD			;
;6--
		movf	rot_bcd+2,w		; Rotation digit   100
		goto	l_BCD			;
;7--
		movf	rot_bcd+3,w		; Rotation digit  1000
		goto	l_BCD			;
;8--
		movf	rot_bcd+4,w		; Rotation digit 10000
		goto	l_BCD			;
;9--
		movlw	6*(' '-' ')		; ' '
		goto	l_dot			;
	#else
;0--
		movlw	6*(' '-' ')		; ' '
		goto	l_dot			;
;1--
		movf	rot_bcd+4,w		; Rotation digit 10000
		goto	l_BCD			;
;2--
		movf	rot_bcd+3,w		; Rotation digit  1000
		goto	l_BCD			;
;3--
		movf	rot_bcd+2,w		; Rotation digit   100
		goto	l_BCD			;
;4--
		movf	rot_bcd+1,w		; Rotation digit    10
		goto	l_BCD			;
;5--
		movf	rot_bcd,w		; Rotation digit     1
		goto	l_BCD			;
;6--
		movlw	low(6*('R'-' ')); 'R'
		goto	l_dot_BCDH_1	;
;7--
		movlw	6*('/'-' ')		; '/'
		goto	l_dot			;
;8--
		movlw	low(6*('m'-' ')); 'm'
		goto	l_dot_BCDH_1	;
;9--
		goto	l_Space			;
	#endif
TestTable1

	IF high(TestTable0) != high(TestTable1)
		ERROR "Test jump table page error"
        ENDIF

;=======================================================
; No execution passes here

l_dot_BCDH_2
		incf	BCD_High,f

l_dot_BCDH_1
		incf	BCD_High,f

l_dot_clr
		bcf		BCD_High,7		;
l_dot
		bcf		STATUS,RP1		; Bank 0 --- For entry from DrawTest
		movwf	BCD				;
		return

l_leading_swap_PM
		call	l_convert_swap
		xorlw	6*('0'-' ')
		btfss	STATUS,Z
		xorlw	6*('0'-' ')
		btfss	STATUS,Z
		goto	l_dot
		goto	l_pm_ap

l_leading_swap
		call	l_convert_swap
		xorlw	6*('0'-' ')
		btfss	STATUS,Z
		xorlw	6*('0'-' ')
		goto	l_dot

l_PM
		btfss	Digit12Mode
		goto	l_Space
		call	Conv2BCD
		andlw	0xF0
		btfsc	STATUS,Z
		goto	l_Space
l_pm_ap
		movlw	.12
		subwf	Hour,w
		btfss	STATUS,C		;
		goto	l_Space
		movlw	6*("'"-' ')		; '''
		goto	l_dot_clr

l_convert_swap
		call	Conv2BCD
l_BCD_swap
		movwf	BCD				;
		swapf	BCD,w			;
		goto	l_BCD
l_convert
		call	Conv2BCD
l_BCD
		andlw	0x0F			;
		bcf		STATUS,RP1		; Bank0
;l_mult6
		movwf	BCD				;
		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	6*('0'-' ')		; Convert to character code, C=0
		btfsc	flags5,bBinMode	; Test for binary mode
		bsf		BCD_High,7
		goto	l_dot

;-------- Update Display memory with Time Ticks every 5 minutes

Ticks
		movf	dpi,w
		xorwf	Scratch3,w
		btfss	STATUS,Z		; At a tick position
		return

		bsf		tdd,bTickLED	; Turn on analog tick Leds

		btfsc	STick12
		goto	Ticks10			; If single tick at 12 always add .10

		movf	Scratch3,w		; Dpi=30 at 12 o clock
TicksCikl
		xorlw	.20
		btfsc	STATUS,Z		; Draw " at 12, ' at other
		goto	Ticks9
		xorlw	.20^.29
		btfsc	STATUS,Z
		goto	Ticks2
		xorlw	.29^.31
		btfsc	STATUS,Z
		goto	Ticks9

		btfss	AnalMode24h		; If analogue hands operate in 24 hour mode
		goto	Ticks10

		xorlw	.31
		addlw	-.60
		btfsc	STATUS,C
		goto	TicksCikl

Ticks10
		movlw	.1
Ticks9
		addlw	.7				; W = 0 if jump to here
Ticks2
		addlw	.2				; W = 0 if jump to here
		addwf	Scratch3,f
		return

;-------- Update Display memory with XII   III  VI  IX around the clock

Draw36912
		movlw	(1 << bShowDDate)|(1 << bShowDTime)|(1 << bShowWDay)
		andwf	flags,w
		btfss	AnalMode24h		; Hide if analogue hands operate in 24 hour mode
		btfss	STATUS,Z		; Hide if digital displaing on
		return

		movf	dpi,f
		btfss	STATUS,Z
		goto	l_Draw_0

		movlw	5
		movwf	dotindex
		movlw	(IX-XII)+low(XII)+2
		movwf	BCD
l_Draw_0
		btfss	dotindex,3
		goto	l_Draw_2
		movf	dpi,w			;
		xorlw	.27				; Are we at XII
		btfsc	STATUS,Z		;
		goto	l_Start_XII		; Start draw it
		xorlw	.27^.58			; Are we at III
		btfsc	STATUS,Z
		goto	l_Start_III		; Start draw it
		xorlw	.58^.88			; Are we at VI
		btfsc	STATUS,Z		;
		goto	l_Start_VI		; Start draw it
		xorlw	.88^.118		; Are we at IX
		btfss	STATUS,Z		;
		goto	l_Draw_1		; No, continue drawing

l_Start_IX						; Calculate index of char
		movlw	IX-VI
l_Start_VI
		addlw	VI-III			; W = 0 if jump to here
l_Start_III
		addlw	III-XII			; W = 0 if jump to here
l_Start_XII
		addlw	low(XII)
		movwf	BCD				; Store table offset, W = 0 if jump to here
		clrf	dotindex		; Start counting dots
l_Draw_1
		btfsc	dotindex,3
		return					; No more dots to draw
l_Draw_2
		incf	dotindex,f		; Move to next dot
		movlw	high(XII)
		call	GetXII			; get the dot pattern for this column
		clrf	PCLATH
		goto	IorToTdd

;******************************************************************************
;	Processing of RC5 command's, called by main program if a command is received
;******************************************************************************

ProcessRC5
		btfss	flags2,bDspEnabled
		goto	ProcessRC5Done	;

	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		;
		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		;
;		btfsc	STATUS,Z		;
;		goto	ProcessRC5Done	;
;		andlw	0x7F			;

;		select_w
		case	TOuterLine 		;
		  goto	ToggleOuterLed	; Outer Line
		case	DigitalMode		;
		  goto	ChgDigitMode
		case	AnalogueMode	;
		  goto	ChgAnalogMode
		case	AnalogueHands	;
		  goto	ChgAnalogHands
		case	DemoM			;
		  goto	ToggleDemo		; Demo Mode
		case	TextMode		;
		  goto	ToggleText		; Text Mode
		case	TextMode		;
		  goto	ToggleText		; Scrolling Text Mode
		case	TStaticText		;
		  goto	ToggleStaticText; Static Text Mode
		case	TWDayName		;
		  goto	ToggleName		; Togge day name display mode
		case	SET_SP			;
		  goto	SetScrollSp		; Set Scrolling speed
		case	TBinMode		;
		  goto	ToggleBinMode	; Toggle binay mode
		case	TLANGUAGE		;
		  goto	SetLanguage		; Set Language
		case	TTicks			;
		  goto	ToggleTicks		; Toggle Ticks mode
		case	STANDBY			;
		  bcf	fDspEnabled		; Disable LEDs
;		default					; If none of listed codes
		goto	ProcessRC5Done

SetScrollSp
		movlw	0xF0			; Set scrolling speed
		addwf	CCPR1H,w
		iorlw	0x0F			; Speeds are 0x0F, 0x1F, ..., 0xEF, 0xFF
		movwf	CCPR1H
		call	ScrollSpToRtc	; Save it to RTC
		goto	ProcessRC5Done	;

SetLanguage						; Set language
		movlw	RTC_Language
		movwf	I2CWordAddr
		incf	Language,w
		andlw	0x0F			; Only 16 language
		movwf	Language
		goto	WriteToRtc		;

ToggleBinMode
		movlw	RTC_flags5
		movwf	I2CWordAddr
		movlw	1 << bBinMode
		xorwf	flags5,w
;		andlw	0x30
		movwf	flags5
		goto	WriteToRtc		;

ToggleName
		movlw	1 << bShowDayName;
		addwf	flags,w
		xorwf	flags,w
		andlw	(1 << bDateFormat) | (1 << bDisp12Mode) | (1 << bShowDayName)
		goto	XorToFlags

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 << bShowOuter	;

XorToFlags
		xorwf	flags,f			;

FlagsToRTC						; Save flags settings to RTC ram
		movlw	RTC_flags		; Address Flags
		movwf	I2CWordAddr
		movf	flags,w
WriteToRtc
		call	I2CByteWrite
		goto	ProcessRC5Done	;

ChgDigitMode
		movlw	1 << bShowWDay
		addwf	flags,f
		goto	FlagsToRTC		;

ChgAnalogHands
		movlw	1 << bShowSecHand;
		addwf	flags3,f
		goto	Flags3ToRTC

ToggleTicks
		movlw	RTC_flags4
		movwf	I2CWordAddr
		movlw	0x10
		addwf	CCP1CON,w
		andlw	0x30
		movwf	CCP1CON
		goto	WriteToRtc		;

ChgAnalogMode
		movlw	1 << bShow36912	;
		addwf	flags3,w
		xorwf	flags3,w
		andlw	(1 << bMinMode) | (1 << bSecMode) | (1 << bShowTicks) | (1 << bShow36912)

XorToFlags3
		xorwf	flags3,f

Flags3ToRTC						; Save flags3 settings to RTC ram
		movlw	RTC_flags3		; Address Flags3
		movwf	I2CWordAddr
		movf	flags3,w
		call	I2CByteWrite
		movf	CCP1CON,w
		goto	WriteToRtc		;

IncDispOffset
		incf	DispOffset,w	;
		call 	CheckIncrement 	;
		goto	SetDispOffset

DecDispOffset
		decf	DispOffset,w	;
		call 	CheckDecrement	;

SetDispOffset					; Save flags settings to RTC ram
		movwf	DispOffset		;
		call	DispOffsToRtc	; Save it to RTC
		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				;
		btfss	Year,7
		goto	TimeSetRC5Done	;
		movwf	Year
		decf	Century,f
		goto	TimeSetRC5Done	;

IncYear							; Increment year -> TimeCheck will correct
		incf	Year,f

TimeSetRC5Done
		bsf		RC5_TimeSet
		bcf		fTimeInv

ProcessRC5Done
		movf	RC5_Cmd,w		;
		movwf	RC5_Cmd2		;
		bsf		NewTime			; force display update
		bcf		RC5_DataReady	;

SetOuterLed
		btfss	fDspEnabled		; Disable LEDs
		goto	OuterLedOff

		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 operation, PORTA and it's copy has to be the same everytime
		bsf		INTCON,GIE		; Enable  interrupts
		return					;

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	;

ToggleText
		call	TextON_OFF		; Scrolling Text mode
		goto	ProcessRC5Done	;

ToggleDemo
		movlw	1 << bDemo		;
		xorwf	flags2,f		;
		goto	ProcessRC5Done	;

;******************************************************************************
; Display mode routines

DemoMode
		movf	Second2,f		;
		btfss	STATUS,Z		;
		return

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

ClearDisplay
		bsf		STATUS,RP1		; jump to bank 2
		movlw	.120
		movwf	ch_dot_index
		movlw	0x20			; set FSR to 0x20
cldisp
		movwf	FSR				;
		clrf	INDF			; clear display memory
		call	CheckIncrementFSR; Increment and test pointer
		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		;

		clrf	ch_flags
		btfsc	fDemo
		bsf		ch_flags,bDemo

	if	(EEADR & 0x180)!=(ch_blanking & 0x180)
		banksel	EEADR			; Bank1
	endif

		movlw	0x80			; If EEPROM_SIZE==256 there are two messages
		andwf	EEADR,f			; Clear address for next read - keep message index

RetBank0
		clrf	STATUS			; goto bank 0
		return


;******************************************************************************
;	Main program
;******************************************************************************

main

InitIO
;		clrf	PORTA
;		clrf	PORTB			; PortA, PortB, PortC cleared at reset vector
;		clrf	PORTC			; all LED OFF

		movlw	0x07
	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	__16F88
		bsf		STATUS,RP1
		clrf	ANSEL			; On 16F88 use all PORTA pins as digital I/O, A/D is off afther reset
		clrf	STATUS
	endif

	ifdef	MotorDirFromCalibration
		btfsc	PORTC,bCalibration; Read Calibration input
		bsf		MotorDirCC		; Set direction of motor rotation
	endif

		bsf		STATUS,RP0		; Bank1

		movlw	~((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 peripheral interrupt enable
		movlw	b'10011000'		; set up timer. prescaler(bit3)bypassed, Int on falling Edge RB0
		movwf	OPTION_REG		; send w to option.
		movlw	.249			; TMR2 period=250 PR2=249
		movwf	PR2				;
		bsf		PIE1,TMR2IE		; enable Peripheral interrupt from TMR2

		clrf	STATUS
InitRam

		movlw	0x20
		movwf	FSR
RamClr
		clrf	INDF
		bsf		STATUS,IRP
		clrf	INDF
		bcf		STATUS,IRP
		incf	FSR,f
		btfss	FSR,7
		goto	RamClr

		bsf		STATUS,RP1		; Bank2

		movlw	0x0F
		movwf	rot_bcd			; Blank rotation BCD data
		movwf	rot_bcd+1		; Wait min. 2 minutes to get first measurement
		movwf	rot_bcd+2
		movwf	rot_bcd+3
		movwf	rot_bcd+4

		bcf		STATUS,RP1		; Bank0

		call	InitSubSecCnt	; reload counter SubSecond = 0x10000 - .2500 = 0xF63C

		call	InitTime		; Init time counter variables and Display flags

		bsf		RC5_Cmd2,6
		bsf		RC5_WaitStart	; RC5 is waiting for startbit

		movf	DispOffset,w
		movwf	iFSR

InitDisp
		call 	ClearDisplay	; clear display content
InitTmr
		clrf	INTCON			; clear and dissable all possible interrupt flag
		clrf	PIR1			; clear all pripheral interrupt flag

		movlw	b'00000101'		; TMR2=ON, Prescaler = 1/4,  Postscaler = 1
		movwf	T2CON			;

		clrf	TMR0			; start timer

		movlw	(1 << T0IE) | (1 << INTE) | (1 << PEIE) | (1 << GIE)
		movwf	INTCON

MainLoop
		btfsc	RC5_DataReady	;
		call	ProcessRC5		;

		btfss	fScrollOn		;
		goto	NoScrolling		;
		movf	TmrScroll,f		;
		btfss	STATUS,Z		;
		goto	NoScrolling		;
		movf	CCPR1H,w		; Get Scrolling speed
		movwf	TmrScroll		; Reinit scrolling timer
		call	ScrollText		;

NoScrolling
		btfsc	NewPCnt			; test Period counter flag
		call	CalcPixelPitch  ; calculate new pixel pitch

		btfss	NewTime			; test new time flag
		goto	MainLoop		;

								; one second past. Do timecheck and if text=off, also a display update.
		call	TimeCheck		;
		call	CalcWDay
		movwf	WDay

		btfsc	fDemo			; Check for demo mode
		call	DemoMode		;

		btfsc	fText			; test Image flag
		goto  	MainLoop		; if Image flag SET, leave display-content unchanged

		btfsc	NewRot			; Convert rotation measurement
		call	Word2Bcd5

								; start display memory update
		clrf	dpi				; dpi = 0
		clrf	Scratch3		; keep index for 5 min timeticks
		clrf	BCD_High
		movlw	0x20			; start of display memory

lUpdateDisplay
		movwf	FSR

		clrf	tdd				;

		btfsc	ShowDTime		;
		call	DTime			; Display digital Time

		btfsc	ShowDDate		;
		call	DDate			; Display digital Date

	#ifndef	VerticalLEDs
		btfsc	ShowHand		; If Hands on
		call	Hands			; Analogue clock's hands

		btfsc	ShowTicks		; If Ticks on
		call	Ticks			; Analogue clock's Ticks
	#endif

		btfsc	Show36912		;
		call	Draw36912		; XII, III, VI, IX around the clock

		movf	flags,w
		andlw	(1 << bShowDDate)|(1 << bShowDTime)|(1 << bShowWDay)
		btfss	STATUS,Z
		goto	StoreTdd
		movf	flags3,w
		andlw	(1 << bShowHourHand)|(1 << bShowMinHand)|(1 << bShowSecHand)|(1 << bShow36912)
		btfss	STATUS,Z
		goto	StoreTdd
		call	DTime			; Display digital Rotation

StoreTdd
		movf	tdd,w			; store data in
		movwf	INDF			; display memory

		incf	dpi,f			;
		movlw	.120			;
		xorwf	dpi,w			;
		btfsc	STATUS,Z		;
		goto  	MainLoop		;

		call	CheckIncrementFSR ; Move pointer
		goto	lUpdateDisplay

;******************************************************************************
;	Text display functions
;******************************************************************************

CharOffset						; Must be called with Bank 2 active
		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

CharGenx						; Calls out of first 2K
		movlw	high(CharTab)	; Add address of char gen table
		addwf	ch_dot_point_H,w; Get high path of index
		movwf	PCLATH			; PCHATH 5.4 = 01
		movf	ch_dot_point_L,w; IT routine MUST save PCLATH
		movwf	PCL

;=======================================================
; No execution passes here

;--------
LoadChrData						; Must be called with Bank 2 active

		call	CharGenx		; Get next dot pattern
		clrf	PCLATH			; Call / Goto in first 2K
		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

;--------
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

		bcf		STATUS,RP1		; Bank0
		return

;--------
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
	#ifdef	VerticalLEDs
		call	FlipPattern
	#endif
		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		;

		goto	RetBank0

;******************************************************************************
;	Some general functions
;******************************************************************************

;******************************************************************************
; 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

		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


;- check correct decrement of Display memory pointer
CheckDecrementFSR
		decf	FSR,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
CheckIncrement
		xorlw	0x48			;
		btfsc	STATUS,Z		;
		movlw	0xA0 ^ 0x48		;
		xorlw	0xF0 ^ 0x48		;
		btfsc	STATUS,Z		;
		movlw	0x20 ^ 0xF0		;
		xorlw	0xF0			;
		return					;

;******************************************************************************
;	I2C RTC functions
;******************************************************************************

InitTime						; Init time from I2C RTC
		clrf	Second2

		movlw	RTC_Tenms		; Address of 1/100 Seconds
		call	I2CByteReadSetAddr; Read seconds
		addlw	-0x50
		btfsc	STATUS,C
		bsf		Second2,0		; Set half seconds
								; tddH = 0x02
								; I2CWordAddr = 0x02
		call	I2CByteReadSetAddr; Read seconds
		call	BcdToBin
		movwf	Second2
		addwf	Second2,f
								; I2CWordAddr = 0x03

		call	I2CByteRead		; Read minute
		call	BcdToBin
		movwf	Minute
								; I2CWordAddr = 0x04

		call	I2CByteRead		; Read hour
		call	BcdToBin3F		; Mask format and AM/PM
		movwf	Hour
								; I2CWordAddr = 0x05

		call	I2CByteRead		; Read day
		call	BcdToBin3F
		movwf	Day
		swapf	tdd,f
		rrf		tdd,f
		rrf		tdd,w
		andlw	0x03
		movwf	dotindex		; Save year bit 1..0
								; I2CWordAddr = 0x06

		call	I2CByteRead		; Read Month
		andlw	0x1F
		call	BcdToBin
		movwf	Month
		swapf	tdd,f
		rrf		tdd,w
		andlw	0x07
		movwf	WDay
								; I2CWordAddr = 0x07

		movlw	RTC_Year		; Address of Year
		call	I2CByteReadSetAddr; Read year
		movwf	Year
								; I2CWordAddr = 0x11

		call	I2CByteRead		; Read year ^ 0xff
		xorlw	0xFF
		xorwf	Year,w
								; I2CWordAddr = 0x12

		btfss	STATUS,Z
		goto	InitTimeDef		; Year not valid, init with default values

		call	I2CByteRead		; Read Century
		movwf	Century
								; I2CWordAddr = 0x13

		call	I2CByteRead		; Read flags
		movwf	flags
								; I2CWordAddr = 0x14

		call	I2CByteRead		; Read DispOffset
		movwf	DispOffset
		call	ValidateOffs
								; I2CWordAddr = 0x15

		call	I2CByteRead		; Read Scroll speed
		iorlw	0xCF
		movwf	CCPR1H
								; I2CWordAddr = 0x16
		call	I2CByteRead		; Read flags3
		movwf	flags3
								; I2CWordAddr = 0x17

		call	I2CByteRead		; Read flags4
		andlw	0x30
		movwf	CCP1CON
								; I2CWordAddr = 0x18

		call	I2CByteRead		; Read language code
		andlw	0x0F
		movwf	Language
								; I2CWordAddr = 0x19

		call	I2CByteRead		; Read flags4
;		andlw	0x30
		movwf	flags5
								; I2CWordAddr = 0x1A

		movf	Year,w			; Has the RTC increment Year
		xorwf	dotindex,w
		andlw	0x03
		btfsc	STATUS,Z
		return					; No, return

		incf	Year,f			; Yes, increment Year
		call	TimeCheck		; Will correct Year and Century
		goto	StoreYear

ValidateOffs
		movlw	0x20
		subwf	DispOffset,w
		btfss	STATUS,C
		goto	DifDispOffs
		movlw	0xF0
		subwf	DispOffset,w
		btfsc	STATUS,C
		goto	DifDispOffs
		movlw	0xA0
		subwf	DispOffset,w
		btfsc	STATUS,C
		return
		movlw	0x48
		subwf	DispOffset,w
		btfss	STATUS,C
		return
		goto	DifDispOffs

InitTimeDef
		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	.20
		movwf	Century

;;1		movlw	(1 << bShowDTime) | (1 << bShowDDate) | (1 << bShowOuter) | (1 << bShowDayName) | (1 << bShowWDay)
		movlw	(1 << bShowDTime) | (1 << bShowDDate) | (1 << bShowOuter) | (1 << bShowWDay)
		movwf	flags			; Init flags

		movlw	(1 << bShowHourHand) | (1 << bShowMinHand) |(1 << bShowSecHand) | (1 << bShowTicks)
		movwf	flags3			; Init flags3
		clrf	CCP1CON			; Init flags4
		clrf	flags5			; Init flags5

		clrf	Language		; Init language

		bsf		fTimeInv

		movlw	RTC_Language	; Address of language in RTC
		movwf	I2CWordAddr
								; I2CWordAddr = 0x18
;;1
;;1 return
		movf	Language,w
		call	I2CByteWrite	; Store it in RTC

; Init display offset with default value
DifDispOffs
		movlw	DISPLAYOFFSET	; Initial Display Offset for the hardware
		movwf	DispOffset		;
		call	DispOffsToRtc

		movlw	0xFF			; Scroll speed
		movwf	CCPR1H

ScrollSpToRtc
		movlw	RTC_ScrollSp	; Address of scrolling speed in RTC
		movwf	I2CWordAddr
								; I2CWordAddr = 0x15
		movf	CCPR1H,w
		goto	I2CByteWrite	; Store it in RTC

DispOffsToRtc
		movlw	RTC_disp_off	; Address DispOffset
		movwf	I2CWordAddr
								; I2CWordAddr = 0x14
		movf	DispOffset,w
		goto	I2CByteWrite

BcdToBin3F
		andlw	0x3F
BcdToBin						; Uses only numbers < 60
		movwf	FSR
		andlw	0x0F
		btfsc	FSR,4
		addlw	.10
		btfsc	FSR,5
		addlw	.20
		btfsc	FSR,6
		addlw	.40
		btfsc	FSR,7
		addlw	.80
		return

StoreTime
		movlw	0x80			; Disable counting command
		call	SendRtcCmd
								; I2CWordAddr = 0x01

		clrw
		btfsc	Second2,0		; Store subsec
		addlw	0x50
		call	I2CBcdWrite
								; tddH = 0x02
		incf	I2CWordAddr,f	; Address of seconds
								; I2CWordAddr = 0x02

		bcf		STATUS,C		; Store Second
		rrf		Second2,w
		call	I2CBcdWrite
								; I2CWordAddr = 0x03

		movf	Minute,w		; Store Minute
		call	I2CBcdWrite
								; I2CWordAddr = 0x04

		movf	Hour,w			; Store Hour
		call	I2CBcdWrite
								; I2CWordAddr = 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	Conv2BCD
		andlw	0x3F
		iorwf	tdd,w
		call	I2CByteWrite
								; I2CWordAddr = 0x06

		swapf	WDay,w			; Store Month and Wday
		movwf	tdd
		rlf		tdd,f
		movlw	0xE0
		andwf	tdd,f
		movf	Month,w
		call	Conv2BCD
		andlw	0x1F
		iorwf	tdd,w
		call	I2CByteWrite
								; I2CWordAddr = 0x07

		movlw	0x00			; Enable counting command
		call	SendRtcCmd
								; I2CWordAddr = 0x01

StoreYear
		movlw	RTC_Year		; Address Year
		movwf	I2CWordAddr
								; I2CWordAddr = 0x10
		movf	Year,w
		call	I2CByteWrite
								; I2CWordAddr = 0x11
		comf	tdd,w
		call	I2CByteWrite
								; I2CWordAddr = 0x12
		movf	Century,w
		goto	I2CByteWrite

SendRtcCmd
		clrf	I2CWordAddr		; I2CWordAddr = 0x00
		goto	I2CByteWrite


;******************************************************************************
;	I2C low level functions
;******************************************************************************

XorToPortA
		xorwf	CCPR1L,f
ToPortA
		movf	CCPR1L,w
		movwf	PORTA
		return

  ifdef	UsePCF8583RTC
; 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 I2CWordAddr, I2CWordAddr incremented afther execution

I2CByteReadSetAddr
								;
		movwf	I2CWordAddr		; Store address

; Reads a byte from RTC to tdd, address is in I2CWordAddr
; I2CWordAddr 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
	IORLW	0x01				; 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	I2CWordAddr,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 I2CWordAddr
; I2CWordAddr incremented afther execution

I2CBcdWrite
	call	Conv2BCD			; Convert w to BCD

; Writes W to RTC, address is in I2CWordAddr
; I2CWordAddr 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 I2CWordAddr
OUT_BYTE_ADDR
	MOVF	I2CWordAddr,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

  else

I2CByteReadSetAddr
								;
		movwf	I2CWordAddr		; Store address
I2CByteRead
I2CBcdWrite
		call	Conv2BCD		; Convert w to BCD
I2CByteWrite
		incf	I2CWordAddr,f
		retlw	RTC_ADDR

  endif

;******************************************************************************
;	Base conversion function for rotation displaying
;******************************************************************************


Word2Bcd5
;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*104 + B3*103 + B2*102 + B1*101 + B0*100
;=B4*10000 + B3*1000 + B2*100 + B1*10 + B0

TenK            EQU rot_bcd+4   ;B4
Thou            EQU rot_bcd+3	;B3
Hund            EQU rot_bcd+2	;B2
Tens            EQU rot_bcd+1	;B1
Ones            EQU rot_bcd		;B0

		bsf		STATUS,RP1		; Bank2
		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	TenK,w			; Remove leading zeros
		movwf	FSR
		movlw	0x0F
		btfsc	STATUS,Z
		movwf	TenK

		movf	Thou,w
		iorwf	FSR,f
		movlw	0x0F
		btfsc	STATUS,Z
		movwf	Thou

		movf	Hund,w
		iorwf	FSR,f
		movlw	0x0F
		btfsc	STATUS,Z
		movwf	Hund

		movf	Tens,w
		iorwf	FSR,f
		movlw	0x0F
		btfsc	STATUS,Z
		movwf	Tens

		bcf		STATUS,RP1		; Bank0
		bcf		NewRot
        return

;******************************************************************************
;	Calculating Day of week using Julian Day Number mod 7 method
;******************************************************************************

CalcWDay							; Calculates day of week
		clrf	Scratch
		clrf	Scratch2
		clrf	Scratch3
		movf	Day,w				; Day changes at 00:00
		movwf	Scratch4

Year16bit							; Convert Century and Year to a 16 bit word in Year16H:Year16L
		movf	Year,w
		bsf		STATUS,RP1			; Bank2
		movwf	Year16L
		clrf	Year16H
		bcf		STATUS,RP1			; Bank0
		movf	Century,w
		bsf		STATUS,RP1			; Bank2
		movwf	BCD
		btfsc	STATUS,Z
		goto	JDN
l_year16
		movlw	.100
		addwf	Year16L,f
		btfsc	STATUS,C
		incf	Year16H,f
		decfsz	BCD,f
		goto	l_year16

JDN									; Returns Julian Day Number
									; Scratch .. Scratch3 cleared before call
									; For WDay and SDate calculation Scratch4 = Day
									; For JDN if Hour < .12 then (Day - 1) else Day

		bcf		STATUS,RP1			; Bank0
		movf	Month,w
		bsf		STATUS,RP1			; Bank2
		addlw	-.3
		movwf	BCD
		btfss	BCD,7
		goto	Dnum
		movlw	.12
		addwf	BCD,f
		bsf		STATUS,IRP			; IRP = a = (.14 - Month) div .12
Dnum								; BCD = m = Month + 12 * a - 3
		call	GetDn
		clrf	PCLATH
		addwf	Scratch4,w			; Add the day
		btfsc	STATUS,C
		incf	Scratch2,f
		movwf	Scratch3			; Scratch2:Scratch3 = day + (.153 * m + 2) div 5
		btfss	STATUS,IRP
		goto	SetY

		decf	Year16L,f
		incf	Year16L,w
		btfsc	STATUS,Z
		decf	Year16H,f			; Year16H:Year16L = .100 * Century + Year - a
SetY
		bcf		STATUS,C
		rrf		Year16H,w
		movwf	BCD
		rrf		Year16L,w
		movwf	Scratch4			; BCD:Scratch4 = (.100 * Century + Year - a) div 2

Mul365
		movlw	low(.365)
		addwf	Scratch3,f
		movlw	high(.365)
		call	ChkCy_2K
		addwf	Scratch2,f
		btfsc	STATUS,C
		incf	Scratch,f
		decfsz	Year16L,f
		goto	Mul365
		decf	Year16H,f
		btfss	Year16H,7
		goto	Mul365
									; Scratch:Scratch2:Scratch3 = day + ((.153 * m +2) div 5) + .356 * (.100 * Century + Year - a)
		movlw	0x1F
		addwf	Scratch3,f
		movlw	0x43
		call	ChkCy_2K
		addwf	Scratch2,f
		movlw	0x1A
		call	ChkCy_2K
		addwf	Scratch,f			; Scratch:Scratch2:Scratch3 = day + ((.153 * m +2) div 5) + .356 * (.100 * Century + Year - a) +
									; .365 * .4800 + (.4800 div 4) - (.4800 div .100) + (.4800 div 400) - .32045
									; C = 0
		rrf		BCD,f
		rrf		Scratch4,f			; BCD:Scratch4 = (.100 * Century + Year - a) div 4

		bcf		STATUS,RP1			; Bank0
		movf	Century,w			; W = (.100 * Century + Year) div .100 = Century
		bsf		STATUS,RP1			; Bank2
		btfss	STATUS,IRP
		goto	Ymod100
		bcf		STATUS,RP1			; Bank0
		movf	Year,f
		bsf		STATUS,RP1			; Bank2
		btfsc	STATUS,Z			; Correct W if Year = 0
		addlw	-1
Ymod100
		movwf	Year16L				; Year16L = W = (.100 * Century + Year - a) div .100
		subwf	Scratch4,f
		btfss	STATUS,C
		decf	BCD,f				; BCD:Scratch4 = (.100 * Century + Year - a) div 4 - (.100 * Century + Year - a) div .100
		rrf		Year16L,f
		rrf		Year16L,w
		andlw	0x3F				; W = (.100 * Century + Year - a) div .400
		addwf	Scratch4,w
		btfsc	STATUS,C
		incf	BCD,f
		addwf	Scratch3,f
		movf	BCD,w
		call	ChkCy_2K
		addwf	Scratch2,f
		btfsc	STATUS,C
		incf	Scratch,f			; Scratch:Scratch2:Scratch3 = (.100 * Century + Year - a) div 4 - (.100 * Century + Year - a) div .100 +  (.100 * Century + Year - a) div .400
		bcf		STATUS,IRP			; Clear IRP

FXD3216UBy700
		clrf	Scratch6			; .256 * JDN / 0x700
		clrf	Scratch4
		movlw	7

FXD3216U_StoreDivH
		movwf	Scratch5			; At return W holds WDay (0 - Monday, 6 - Sunday)

;Inputs:
;   Dividend - Scratch:Scratch2:Scratch3:Scratch4 - Scratch is the most significant
;   Divisor  - Scratch5:Scratch6
;Temporary:
;   Counter  - BCD
;   Remainder- Year16H:Year16L
;Output:
;   Quotient - Scratch:Scratch2:Scratch3:Scratch4
;

FXD3216U							; Call with Bank2 selected
		clrf	Year16L				; Clear result
		clrf	Year16H
		movlw	.32
		movwf	BCD

LOOPU3216
		rlf		Scratch4,F
		rlf		Scratch3,F			; Shift left divider to pass next bit to remainder
		rlf		Scratch2,F			; and shift in next bit of result
		rlf		Scratch,F

		rlf		Year16L,F			; Shift carry into remainder
		rlf		Year16H,F

		rlf		BCD,F				; Save carry in counter

		movf	Scratch6,W			; Substract divisor from remainder
		subwf	Year16L,F
		movf	Scratch5,W
		btfss	STATUS,C
		incf	Scratch5,W
		subwf	Year16H,W			; Keep that byte in W untill we make sure about borrow

		btfsc	STATUS,C
		bsf		BCD,0				; Set bit 0 of counter (saved carry)

		btfsc	BCD,0				; If no borrow
		goto	UOK46LL				; jump

		movf	Scratch6,W			; Restore remainder if borrow
		addwf	Year16L,F
		movf	Year16H,W			; Read high byte of remainder to W
									; not to change it by next instruction
UOK46LL
		movwf	Year16H				; Store high byte of remainder
		bcf		STATUS,C			; Copy bit 0 to carry
		rrf		BCD,F				;  and restore counter
		decfsz	BCD,f				; Decrement counter
		goto	LOOPU3216			;  and repeat loop if not zero

		rlf		Scratch4,f			; Shift in last bit of result
		rlf		Scratch3,F
		rlf		Scratch2,F
		rlf		Scratch,F

		clrf	STATUS				; Return with Bank0
		return

ChkCy_2K
		btfsc	STATUS,C			; Take care of C
		addlw	1
		return

;******************************************************************************
; Get number of days for JDN

GetDn
		movlw	high(JDNTabl)
		movwf	PCLATH
		movf	BCD,w
		addlw	low(JDNTabl)
		movwf	PCL

;******************************************************************************
		org	0xC60

JDNTabl
; 	(.153 * m + 2) div 5, Add 256 for last 3 in Scratch2:W
;	m	 0     1     2     3    4     5     6     7     8      9         10         11
		DT	.0  , .31 , .61 , .92 ,.122 ,.153 ,.184 ,.214 ,.245 ;;.256+.19 , .256+.50 , .256+.81
		addlw	low(.19-.49)					;  9
		addlw	low(.50-.80)					; 10
JDNTabl11
		addlw	low(.81-low(JDNTabl11))	; 11
		incf	Scratch2,f
		return

	END    ; directive 'end of program'
