	list	b=4
;************************************************************************
; Soubry Henk wrote:													*
; "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.																*
; This 16f628 code is for the stationary part of the clock and will		*
; drive the primary transformer coil, and use RC5 to switch the motor	*
; on and off (and adjust the speed)"									*
;																		*
; - Rewritten, extended to an alarm clock:								*
;	- Alarm timing (hour and minute with day of week masking)			*
;	  20 on 16F628(A) and 16F648A, 40 on 16F87 and 16F88,				*
;	- Day of week is calculated from Julian Day Number					*
;	- Alarm sound and relais drive output,								*
;	- Push button wake up/go standby, automatic standby timer,			*
;	- Wake up in Run / Standby - Sleep / Deap sleep mode,				*
;	- Deap sleep mode - No power on coil for propellers with RTC,		*
;	- Date, Time, Alarm data and settings stored in PCF8583 RTC,		*
;	- Automatic time synchronization to RTC's time,						*
;	- DCF77 time synchronization,										*
;	- Error led to sign time, alarm data invalid,						*
;	- I2C bus @ 5V for handling other devices (LCD display),			*
;	- Push button to turn on/off relais, adjustable relais on time,		*
;	- Sound at hour change with silent period,							*
;	- Motor PWM,														*
;	- Extended IR command set,											*
;	- Infra commands to switch on/off the relais with adjustable on time*
;	- RS232 channel to connect to PC,									*
;	- Doubble buffered infra RC5 receiver,								*
;																		*
;	- Fixed PR2 values .49, improuved time counting with TMR2 since 2.11*
;																		*
;************************************************************************
;																		*
;	 Filename:		base.asm											*
;	 Date:			20/08/2002											*
#define LastUpdate	"2014-03-19";										*
#define	VersionMajor	0x02	;										*
#define	VersionMinor	0x18	;										*
;																		*
;  Based on:															*
;	 Author:		Soubry Henk											*
;	 Company:		Soubry Software Service								*
;																		*
;  Rewritten - Extended:												*
;	 Hp41C		www.hobbielektronika.hu									*
;																		*
;************************************************************************
;																		*
;	 Files required:													*
;			keys.asm													*
;																		*
;************************************************************************
;																		*
;	 Notes:																*
;		Pin assignment													*
;			Port A			  common for 16F628 and 16F87-88			*
;				RA0 = SCL line of I2C bus, use 2k7 pullup to pic's Vdd	*
;				RA1 = SDA line of I2C bus, use 2k7 pullup to pic's Vdd	*
;				RA2 = Ir receiver										*
;				RA3 = Reais push button									*
;				RA4 = Wake up push button					open drain	*
;				RA5 = DCF77 active low input				input only	*
;																		*
;			Port B						16F628(A)-648A		16F87-88	*
;				Index LED output					RB0		RB0			*
;				UART RxD							RB1		RB2			*
;				UART TxD							RB2		RB5			*
;				CCP1 PWM output - FET GATE for coil	RB3		RB3			*
;				Motor Shutdown output				RB4		RB4			*
;				Alarm sound output					RB5		RB1			*
;				Relais drive output					RB6		RB6			*
;				Error LED output					RB7		RB7			*
;																		*
;************************************************************************
;																		*
;  RTC Ram layout														*
;	00	01	02	03	04	05	06	07	08	09	0A	0B	0C	0D	0E	0F		*
;	cmd		sec	min	hou	day	mon											*
;						y10	wda											*
;																		*
;	10	11	12	13	14	15	16	17	18	19	1A	1B	1C	1D	1E	1F		*
;	Yea	~Y	Ccp	Pr2	Ccp Pr2	AST	AST	HS	HS	Cen	Rel			Mo	Mo2		*
;			run	run	sl	sl	H	L	beg	end		ir						*
;																		*
;	20	21	22	23	24	25	26	27	28	29	2A	2B	2C	2D	2E	2F		*
;	-- Alarm 0	--	-- Alarm 1	--	-- Alarm 2	--	-- Alarm 3	--		*
;	WDa	Hou	Min	Md	WDa	Hou	Min	Md	WDa	Hou	Min	Md	WDa	Hou	Min	Md		*
;																		*
;	30	31	32	33	34	35	36	37	38	39	3A	3B	3C	3D	3E	3F		*
;	-- Alarm 4	--	-- Alarm 5	--	-- Alarm 6	--	-- Alarm 7	--		*
;	WDa	Hou	Min	Md	WDa	Hou	Min	Md	WDa	Hou	Min	Md	WDa	Hou	Min	Md		*
;																		*
;	40	41	42	43	44	45	46	47	48	49	4A	4B	4C	4D	4E	4F		*
;	-- Alarm 8	--	-- Alarm 9	--	-- Alarm 10 --	-- Alarm 11 --		*
;	WDa	Hou	Min	Md	WDa	Hou	Min	Md	WDa	Hou	Min	Md	WDa	Hou	Min	Md		*
;																		*
;	50	51	52	53	54	55	56	57	58	59	5A	5B	5C	5D	5E	5F		*
;	-- Alarm 12 --	-- Alarm 13 --	-- Alarm 14 --	-- Alarm 15 --		*
;	WDa	Hou	Min	Md	WDa	Hou	Min	Md	WDa	Hou	Min	Md	WDa	Hou	Min	Md		*
;																		*
;	60	61	62	63	64	65	66	67	68	69	6A	6B	6C	6D	6E	6F		*
;	-- Alarm 16 --	-- Alarm 17 --	-- Alarm 18 --	-- Alarm 19 --		*
;	WDa	Hou	Min	Md	WDa	Hou	Min	Md	WDa	Hou	Min	Md	WDa	Hou	Min	Md		*
;																		*
;	70	71	72	73	74	75	76	77	78	79	7A	7B	7C	7D	7E	7F		*
;	-- Alarm 20 --	-- Alarm 21 --	-- Alarm 22 --	-- Alarm 23 --		*
;	WDa	Hou	Min	Md	WDa	Hou	Min	Md	WDa	Hou	Min	Md	WDa	Hou	Min	Md		*
;																		*
;	80	81	82	83	84	85	86	87	88	89	8A	8B	8C	8D	8E	8F		*
;	-- Alarm 24 --	-- Alarm 25 --	-- Alarm 26 --	-- Alarm 27 --		*
;	WDa	Hou	Min	Md	WDa	Hou	Min	Md	WDa	Hou	Min	Md	WDa	Hou	Min	Md		*
;																		*
;	90	91	92	93	94	95	96	97	98	99	9A	9B	9C	9D	9E	9F		*
;	-- Alarm 28 --	-- Alarm 29 --	-- Alarm 30 --	-- Alarm 31 --		*
;	WDa	Hou	Min	Md	WDa	Hou	Min	Md	WDa	Hou	Min	Md	WDa	Hou	Min	Md		*
;																		*
;	A0	A1	A2	A3	A4	A5	A6	A7	A8	A9	AA	AB	AC	AD	AE	AF		*
;	-- Alarm 32 --	-- Alarm 33 --	-- Alarm 34 --	-- Alarm 35 --		*
;	WDa	Hou	Min	Md	WDa	Hou	Min	Md	WDa	Hou	Min	Md	WDa	Hou	Min	Md		*
;																		*
;	B0	B1	B2	B3	B4	B5	B6	B7	B8	B9	BA	BB	BC	BD	BE	BF		*
;	-- Alarm 36 --	-- Alarm 37 --	-- Alarm 38 --	-- Alarm 39 --		*
;	WDa	Hou	Min	Md	WDa	Hou	Min	Md	WDa	Hou	Min	Md	WDa	Hou	Min	Md		*
;																		*
;	C0	C1	C2	C3	C4	C5	C6	C7	C8	C9	CA	CB	CC	CD	CE	CF		*
;																		*
;	D0	D1	D2	D3	D4	D5	D6	D7	D8	D9	DA	DB	DC	DD	DE	DF		*
;																		*
;	E0	E1	E2	E3	E4	E5	E6	E7	E8	E9	EA	EB	EC	ED	EE	EF		*
;	--------------    D  C  F  7  7    D  A  T  A    -------------		*
;	00  01  02  03  04  05  06  07  08  09  10  11  12  13  14  15		*
;																		*
;	F0	F1	F2	F3	F4	F5	F6	F7	F8	F9	FA	FB	FC	FD	FE	FF		*
;	--------------    D  C  F  7  7    D  A  T  A    -------------		*
;	16  17  18  19  20  21  22  23  									*
;																		*
;************************************************************************

; Disabling messages
		errorlevel	-302

	ifdef	__16F628
		list	p=16f628			  ; 16F628 can be used
		#include <p16f628.inc>		  ; processor specific variable definitions
EEPROM_SIZE	EQU	128
#define PROCCODE	.28
#define ProcType	"16F628"
	endif
	ifdef	__16F628A
		list	p=16f628A			  ; 16F628A can be used
		#include <p16f628A.inc>		  ; processor specific variable definitions
EEPROM_SIZE	EQU	128
#define PROCCODE	.28
#define ProcType	"16F628A"
	endif
	ifdef	__16F648A
		list	p=16f648A			  ; 16F648A can be used
		#include <p16f648A.inc>		  ; processor specific variable definitions
EEPROM_SIZE	EQU	256
#define ProcType	"16F648A"
#define PROCCODE	.48
	endif
	ifdef	__16F87
		list	p=16f87				  ; 16F87 can be used
		#include <p16f87.inc>		  ; processor specific variable definitions
		#define	T0IE	TMR0IE
		#define	T0IF	TMR0IF
EEPROM_SIZE	EQU	256
#define ProcType	"16F87"
#define PROCCODE	.87
	endif
	ifdef	__16F88
		list	p=16f88				  ; 16F88 can be used
		#include <p16f88.inc>		  ; processor specific variable definitions
		#define	T0IE	TMR0IE
		#define	T0IF	TMR0IF
EEPROM_SIZE	EQU	256
#define ProcType	"16F88"
#define PROCCODE	.88
	endif

	ifndef	EEPROM_SIZE
		error	"Incompatible processor type selected"
	endif

	#include "keys.asm"

	ifdef	__16F88
		__CONFIG _CONFIG1, _CP_OFF & _CCP1_RB3 & _WRT_PROTECT_OFF & _WDT_OFF & _BODEN_ON & _PWRTE_ON & _HS_OSC & _MCLR_OFF & _LVP_OFF
		__CONFIG _CONFIG2, _IESO_OFF & _FCMEN_OFF
	else
	  ifdef	__16F87
		__CONFIG _CONFIG1, _CP_OFF & _CCP1_RB3 & _WRT_PROTECT_OFF & _WDT_OFF & _BODEN_ON & _PWRTE_ON & _HS_OSC & _MCLR_OFF & _LVP_OFF
		__CONFIG _CONFIG2, _IESO_OFF & _FCMEN_OFF
	  else
		__CONFIG _CP_OFF & _WDT_OFF & _BODEN_ON & _PWRTE_ON & _HS_OSC & _MCLRE_OFF & _LVP_OFF
	  endif
	endif


	__idlocs		0xB000 | (VersionMajor << 8) | (VersionMinor)


#define RC5AddrCheck			; Comment this line if RC5 Address checking not required

; Default mode settings at first power up
#define	AutoStandby				; Comment this line to disable auto standby
;#define	WakeUpRun				; Comment this line if wake up in sleep mode
#define	DeapSleeping			; Comment this line if coil has to be powered in sleep mode
;#define	TimeReInit				; Comment this line not to reinit time from RTC at midnight
;#define	EnableHourSign			; Comment this line to disable sound at hour change
;#define	SignMode				; Comment this line to enable as many beeps as hour (1..12)
#define	DCF77Enabled			; Comment this line to disable DCF77 decode
;#define	InvertSpeedControl			; Commant this line for low active motor control

; Serial port Baud rate selection
;BaudRate	EQU	.2400			; Set the BaudRate of RS232 communication
;BaudRate	EQU	.4800			; Set the BaudRate of RS232 communication
;BaudRate	EQU	.9600			; Set the BaudRate of RS232 communication
BaudRate	EQU	.19200			; Set the BaudRate of RS232 communication
;BaudRate	EQU	.38400			; Set the BaudRate of RS232 communication

#define	RTC_ADDR		0xA0	; RTC's address with A0 pin grounded
#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_PWMData		0x12	; Address of PWM data
#define	RTC_AST			0x16	; Address of AutoStandbyTimer
#define	RTC_HourSnd		0x18	; Address of hour sound parameters
#define RTC_Century		0x1A	; Address of century
#define RTC_RelOnIr		0x1B	; Address of Relais on time for ir commands
;						0x1C
;						0x1D
#define	RTC_Mode		0x1E	; Address of mode
#define	RTC_Mode2		0x1F	; Address of mode2
#define	RTC_AlarmData	0x20	; Address of alarm data (.20*4 or .40*4 bytes)
#define RTC_DCFData		0xE0

; PWM settings for Sleep mode
SPEEDSLEEP	EQU	0xC0
CCPR1LSleep	EQU	0x0C

; PWM settings for Run mode
SPEEDRUN	EQU	0xFF
CCPR1LRUN	EQU	0x28


F_OSC	EQU	.20000000			; Oscillator frequency in Hz
OSC		EQU	F_OSC/4

	ifndef BaudRate
		error "No BaudRate defined"
	endif
	if ( (BaudRate!=.2400) && (BaudRate!=.4800) && (BaudRate!=.9600) && (BaudRate!=.19200) && (BaudRate!=.38400) )
		error "Nonstandard BaudRate defined"
	endif

;***** VARIABLE DEFINITIONS

;Vars in shared memory. This memory is available from any bank

  cblock	0x070
	Scratch			; memory locations for general use
	Scratch2		;
	Scratch3		; Alarm index
	Scratch4
	BCD				; BCD routine temp var, I2C delay counter

	I2CSlaveAdr		; I2C slave address of current device
	I2CWordAdr		; I2C word address of current transfer
	I2CBitCnt		; I2C bit counter of data in/out routines
	I2CShiftReg		; I2C data shift register

	flags			;
	flags2
	Mode			;
	Mode2

	w_temp			; variable used for context saving
	status_temp		; variable used for context saving
	fsr_temp		; variable used for context saving
LastCommon:
  endc
	IF LastCommon > 0x80
		ERROR "To many variables used in Common RAM"
	ENDIF

  cblock	0x20
;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

	SubSec_L		; Sub second timer
	SubSec_H

	SleepTimer		; Down counter in second, started at standby.

	StandbyTimerL	; Down counter for auto standby
	StandbyTimerH	;

	SoundCnt		; Down counter for alarm sound generation
	SwitchCnt		; Down counter for relais drive

	AutoStbyTimL	; Auto Standby time in seconds
	AutoStbyTimH	;

	Speed_Sleep		; Speed for sleep mode
	Speed_Run		; Speed	for run   mode

	CCP1_Sleep		; PWM pulse width for sleep mode
	CCP1_Run		; PWM pulse width for run	mode

	RC5_flags		; RC5 decoding state flags
	RC5_Tmr			; RC5 time counter
	RC5_Addr		; Address field of RC5 message just received
	RC5_Cmd			; Command field of RC5 message just received
	RC5_Cmd2		; storage for previous cmd
	RC5_ShrA
	RC5_ShrC

	Alarms			; Number of alarms can be used on processor

	UartRxChNum		; Uart Rx fifo number of available chars
	UartRxChWp		; Uart Rx fifo write pointer
	UartRxChRp		; Uart Rx fifo read	 pointer

	UartTxChNum		; Uart Tx fifo number of available chars
	UartTxChWp		; Uart Tx fifo write pointer
	UartTxChRp		; Uart Tx fifo read	 pointer

	Rs232Cmd		; Last command code received from uart
	Rs232ParNum		; Number of paraneters of command
	Rs232Flags		; Command decoding state flags

	Rs232StrPtr		; 16 bit pointer to string to be sent
	Rs232StrPtrH
	Rs232Temp		; Temporary storage in UART routines
	Rs232Temp2		; Temporary storage in UART routines
	Rs232Fsr		; Save for FSR in UART routines
	Rs232Status		; Save for STATUS in UART routines

	RelOnIr			; Relais on time for ir and push button commands (minute)

	WuBtCnt			; Button counter for Wake Up/Go Sleep
	RyBtCnt			; Button counter for relais On/Off

	HourSndBeg		; Start of silent period
	HourSndEnd		; End   of silent period
	Speed			; Motor speed
	SpeedSheed		; Variable for motor PWM

	LastMinute		; Last minute processed

; DCF77 decoding
	DCF_bitcntr		; Bit counter 0 .. 59
	DCF_timer		; Timer using 10 ms time base
	DCF_flags		; DCF77 decoding sate flags
	DCF_20ms_cnt	; Counts 200uS interrupts to generate 20 ms time base - Power line nois reduction
	DCF_temp		; A variable for filtering
	DCF_SNum		; Number of succesfull synchronizations
	DCF_PNum		; Number of succesfull synchronizations in previous hour

; DCF77 decoding - Time	BCD encoding used
  ;!! Keep variable order - indirect access used to them v
	DCF_Minute		; 0 .. 59
	DCF_Hour		; 0 .. 23
	DCF_Day			; 1 .. 31
	DCF_Month		; Bit 7..3: Month	1 .. 12, bit 2..0: Wday 1..7
	DCF_Year		; 0 .. 99
  ;!! Keep variable order - indirect access used to them ^

LastBank0:
  endc
	IF LastBank0 > 0x70
		ERROR "To many variables used in Bank0"
	ENDIF

;Vars indirectly accessed are on Bank1
  cblock	0x0A0	; Alarm table - Indirectly accessed
	Alarm0WDay		; Bits 7: Global enable, 6..0: Enable on Day of week 7..1
	Alarm0Hour		; 0..23 or 32 for don't care
	Alarm0Minute	; 0..59
	Alarm0Mode		; Bit 7: 0 - Alarm:
							; Bit 6: turn propeller on
							; Bit 5: turn propeller off
							; Bit 4: Enable sound
					; Bit 7: 1 - Relais switched on:
							; Bit 6..0: turn on time in minutes

	Alarm1:4
	Alarm2:4
	Alarm3:4
	Alarm4:4
	Alarm5:4
	Alarm6:4
	Alarm7:4
	Alarm8:4
	Alarm9:4
	Alarm10:4
	Alarm11:4
	Alarm12:4
	Alarm13:4
	Alarm14:4
	Alarm15:4
	Alarm16:4
	Alarm17:4
	Alarm18:4
	Alarm19:4

	; Not to use memory 0x0F0..0x0FF  -	 it will overwrite 0x070..0x07F
LastBank1:
  endc
	IF  LastBank1 > 0xF0
		ERROR "To many variables used in Bank1"
	ENDIF

  cblock	0x120	; Uart buffers
					; More RAM space available: 48 for 16F628(A) and 80 for 16F684A ,16F87, 16F88
	UartTxChar:	.32	; Uart Tx fifo

	UartRxChar:	.16	; Uart Rx fifo
	; Not to use memory 0x170..0x17F  -	 it will overwrite 0x070..0x07F
LastBank2:
  endc


	ifdef	__16F628
	  IF  LastBank2 > 0x150
		ERROR "To many variables used in Bank2"
	  ENDIF
	endif
	ifdef	__16F628A
	  IF  LastBank2 > 0x150
		ERROR "To many variables used in Bank2"
	  ENDIF
	endif

	IF	LastBank2 > 0x170
		ERROR "To many variables used in Bank2"
	ENDIF

	ifdef	TMR0IE
;Vars indirectly accessed are on Bank3 on 16F87 and 16F88
  cblock	0x1A0	; Alarm table - Indirectly accessed
	Alarm20:4
	Alarm21:4
	Alarm22:4
	Alarm23:4
	Alarm24:4
	Alarm25:4
	Alarm26:4
	Alarm27:4
	Alarm28:4
	Alarm29:4
	Alarm30:4
	Alarm31:4
	Alarm32:4
	Alarm33:4
	Alarm34:4
	Alarm35:4
	Alarm36:4
	Alarm37:4
	Alarm38:4
	Alarm39:4

	; Not to use memory 0x1F0..0x1FF  -	 it will overwrite 0x070..0x07F
LastBank3:
  endc

	IF  LastBank3 > 0x1F0
		ERROR "To many variables used in Bank3"
	ENDIF

	endif

;**********************************************************************

; PORTA bits
bSCL			EQU	0			; I2C SCL line
bSDA			EQU	1			; I2C SDA line
bRC5inp			EQU	2			; IR receiver's output
bButton2		EQU	3			; Menu button
bButton			EQU	4			; Power up push button
bDCF77inp		EQU	5			; DCF77 input

; PORTB bits
bLED			EQU	7			; Visible LED output
bRelais			EQU	6			; Relais drive output
bMotorOFF		EQU	4			; Motor controll output
bTrafo			EQU	3			; PWM controll of coil
bIndexLed		EQU	0			; Index LED output

; Difference in pinout
	ifdef	__16F88
bTxD			EQU	5			; UART's TxD line
bRxD			EQU	2			; UART's RxD line
bSoundOut		EQU	1			; Buzzer output
	else
bSoundOut		EQU	5			; Buzzer output
bTxD			EQU	2			; UART's TxD line
bRxD			EQU	1			; UART's RxD line
	endif

; Flags
bStandBy		EQU	0			; Standby mode
bSleeping		EQU	1			; Sleep mode
bCoilPower		EQU	2			; High power on coil
bIndexOn		EQU	3			; Index LED on
bButtonDn		EQU	4
bButton2Dn		EQU	5
bNewTime		EQU	6			; Check the time
bTimeInv		EQU	7			; Time invalid

; Flags2
;				EQU	0
bNight			EQU	1			; Night period
bDark			EQU	2			; Light measurement measures dark
bLowSpeed		EQU	3			; Motor rubs at low speed
;				EQU	4
;				EQU	5
;				EQU	6
bI2CBusError	EQU	7			; I2C bus error

; RC5_flags
bRC5_WaitStart	EQU	0
bRC5_DataReady	EQU	1
bRC5_prev_inp	EQU	bRC5inp		; Has to be on the same bit position as bRC5inp
bRC5_Idle		EQU	3
bRC5_HalfBit	EQU	4
;				EQU	5
bRC5_ReSynced	EQU	6
;				EQU	7

; RS232Flags
bCmdExec		EQU	7			; Execution of a command is in progress
bTimeSet		EQU 6			; A date/time setting command was received

; Mode bits
bAutoStby		EQU	0			; Auto standby timer enable
bWakeRun		EQU	1			; Wake Run afther power up
bDeapSleep		EQU	2			; No coil drive in standby mode
bHourSign		EQU	3			; Sound at hour change
bHourSignMode	EQU	4			; Hour sign mode (One sound / as many as hour (1..12))
;				EQU	5
bDCFEnabled		EQU	6			; DCF77 synchron enabled
bReinitTime		EQU	7			; Enable reinit time from RTC

; Mode2
bMotorModeEn	EQU	0			; Motor PWM enabled
bMotorModeMan	EQU	1			; Manual speed setting
bMotorModeLight	EQU	2			; Light controlled speed setting
bLowSpeed		EQU	3			; Manual speed
;				EQU	4
;				EQU	5
;				EQU	6
;				EQU	7

; DCF77_Flags
bDCFBit			EQU		bDCF77inp; Last level read, has to be on same bit as on PORT
bDCFError		EQU		1		; Error in decoding
bDCFParityErr	EQU		2		; Parity error
bDCFParity		EQU		3		; Bit for calculating even parity
bDCFWaitSync	EQU		4		; Wait for DCF77 minute synchron
bDCFReady		EQU		0		; DCF time and date ready and valid
bDCFSummerTime	EQU		6		; CEST time used / Summer Daylight saving time
bDCFData		EQU		7		; Decoded data bit, rlf used to shift to C

; Alarms						; Number of alarm enrties available
;	Bit5		0				; Bank3 not available: 16F628(A)  , 16F648A :.20=0x14
;	Bit5		1				; Bank3		available: 16F87	  , 16F88	:.40=0x28

;******************************************************************************
;	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
;
;**********************************************************************
		org		0x0000				; processor reset vector
		clrf	PORTA				; Clear output for I2C SCL & SDA
		clrf	PORTB				;
	#ifdef	InvertSpeedControl
		movlw	(1<<bLED)|(1<<bIndexLed);Turn off error and index led, motor
	#else
		movlw	(1<<bLED)|(1<<bIndexLed)|(1<<bMotorOFF);Turn off error and index led, motor
	#endif
		goto	main				; go to beginning of program

;**********************************************************************
		org		0x0004				; interrupt vector location

		movwf	w_temp				; context saveing
		swapf	w_temp, f
		swapf	STATUS, w

		clrf	STATUS				; select Bank0, IRP=0 for interrupt stuff
		movwf	status_temp


INT_TMR2
		btfss	PIR1,TMR2IF			; Interrupt on TMR2 with postcaler = 1/5; PWM uses 40uS period
		goto	lTMR2EXIT			; nope, goto next interrupt

		bcf		PIR1,TMR2IF			; Clear TMR2 interrupt flag

		btfss	Mode2,bMotorModeEn	; If motor PWM enabled
		goto	ChkSound

		movf	Speed,w				; PWM for motor
		addwf	SpeedSheed,f
		swapf	STATUS,w
	#ifdef	InvertSpeedControl
		xorlw	1 << bMotorOFF
	#else
		xorlw	0
	#endif
		btfsc	flags,bStandBy
		clrw
		xorwf	PORTB,w
		andlw	1 << bMotorOFF
		xorwf	PORTB,w

ChkSound
		btfsc	Second2,0			; Chopp alarm sound output
		goto	OffSound

		movf	SoundCnt,f			; Counter == 0 ?
		btfsc	STATUS,Z
		goto	OffSound			; Yes - Turn off sound

		bsf		PORTB,bSoundOut		; Turn on sound output
		goto	CountSubSec

OffSound
		bcf		PORTB,bSoundOut		; Turn off sound output

CountSubSec

		incfsz	SubSec_L,f			; Increment 16-bit Sub Second counter
		goto	lTime_1				; stil counting
		incfsz	SubSec_H,f			;
		goto	lTime_1				; stil counting

		movlw	0x3C				; load counter SubSecond = 0x10000 - 2500 = 0xF63C
		movwf	SubSec_L			;
		movlw	0xF6				;
		movwf	SubSec_H			;

		incf	Second2,f			; Move time
		bsf		flags,bNewTime		; Sign new time for TimeCheck

		movlw	1<<bLED				; Toggle visible led if time read from RTC invalid
		btfsc	flags,bTimeInv
		xorwf	PORTB,f
		btfss	flags,bTimeInv		; If time valid
		bsf		PORTB,bLED			; Turn off visible led

		btfsc	Second2,0			; Second passed
		goto	lTime_1

		movf	SoundCnt,f			; If SoundCnt != 0
		btfsc	STATUS,Z
		goto	lChkStby

		decf	SoundCnt,f			; Decrement sound counter

lChkStby
		btfss	flags,bStandBy		; In standby?
		goto	lTime_2				; no, count down StandbyTimer
		movf	SleepTimer,f		;
		btfsc	STATUS,Z			; SleepTimer = 0 ?
		goto	lTime_1				; yes, stop countdown
		decf	SleepTimer,f		; count down sleeptimer in seconds steps
		goto	lTime_1

lTime_2
		movf	StandbyTimerL,w		; If 16 bit StandbyTimer != 0
		iorwf	StandbyTimerH,w
		btfsc	STATUS,Z
		goto	lTime_1

		decf	StandbyTimerL,f		; Decrement it
		incf	StandbyTimerL,w
		btfss	STATUS,Z
		goto	lTime_1
		decf	StandbyTimerH,f

lTime_1
		movf	Second2,w			; Check for minute change
		xorlw	.120
		btfss	STATUS,Z
		goto	lRC5_1

		movf	SwitchCnt,f			; Check relais switch counter
		btfsc	STATUS,Z
		goto	lRC5_1				; Skip if counter == 0

		decfsz	SwitchCnt,f			; Decrement timer
		goto	lRC5_1

		bcf		PORTB,bRelais		; Turn off relais

lRC5_1								; start RC5 stuff here
		btfsc	RC5_flags,bRC5_DataReady
		goto	lRC5_Exit
		btfss	RC5_flags,bRC5_Idle
		goto	lRC5_Not_Idle		;
		decfsz	RC5_Tmr,f			;
		goto	lRC5_Exit			;
		btfsc	PORTA,bRC5inp		; test input
		bcf		RC5_flags,bRC5_Idle	; input = high, cancel Idle state
		incf	RC5_Tmr,f			; continue Idle state until input = high
		goto	lRC5_Exit			;

lRC5_Not_Idle
		btfss	RC5_flags,bRC5_WaitStart;
		goto	lRC5_ReSync			;

lRC5WaitStart
		btfsc	PORTA,bRC5inp		; test input
		goto	lRC5_Exit			; no startbit
		bcf		RC5_flags,bRC5_WaitStart; start received
		movlw	.4					; 13 bits to receive
		movwf	RC5_ShrC			;
		clrf	RC5_ShrA			;
		movlw	.6					;
		goto	lRC5_Reload			;

lRC5_ReSync							;
		movf	PORTA,w				;
		xorwf	RC5_flags,w			;
		andlw	1 << bRC5inp			;
		btfsc	STATUS,Z			;
		goto	lRC5_no_sync		;
		bsf		RC5_flags,bRC5_ReSynced;
		xorwf	RC5_flags,f			; Save new input in RC5_flags
		movlw	.6					; re-sync the timer
		btfss	RC5_flags,bRC5_HalfBit;
		movlw	.2					;
		movwf	RC5_Tmr				;

lRC5_no_sync
		btfsc	RC5_flags,bRC5_HalfBit;
		goto	lRC5_2nd_Half		;

lRC5_1st_Half
		decfsz	RC5_Tmr,f			;
		goto	lRC5_Exit			;
		bcf		STATUS,C			;
		btfsc	PORTA,bRC5inp		;
		bsf		STATUS,C			; C = RC5inp
		rlf		RC5_ShrC,f			;
		rlf		RC5_ShrA,f			;
		bsf		RC5_flags,bRC5_HalfBit; indicate that the first half bit is received
		bcf		RC5_flags,bRC5_ReSynced;
		movlw	.4					;
		goto	lRC5_Reload

lRC5_2nd_Half
		btfsc	RC5_flags,bRC5_ReSynced;
		goto	lReSyncOK			;
		decfsz	RC5_Tmr,f			;
		goto	lRC5_Exit			;

lRC5_Error
		movf	RC5_flags,w
		andlw	(1 << 7) | (1 << 5) | (1 << bRC5_DataReady)
		iorlw	(1 << bRC5_WaitStart) | (1 << bRC5_Idle)
		movwf	RC5_flags
		movlw	.128				;
lRC5_Reload
		movwf	RC5_Tmr				;
		goto	lRC5_Exit			;

lReSyncOK							; test second bit half
		bcf		RC5_flags,bRC5_HalfBit;
		btfss	RC5_ShrA,7			;
		goto	lRC5_Exit			;
		rlf		RC5_ShrC,f			; Shift left to complete Addr
		rlf		RC5_ShrA,f			;
		rlf		RC5_ShrC,w			; RC5_Cmd remains unchanged
		rlf		RC5_ShrA,f			; Complete address
		bcf		RC5_ShrC,7			;
		btfss	RC5_ShrA,6			;
		bsf		RC5_ShrC,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_ShrA,5			;
		bsf		STATUS,C			; C = ToggleBit
		rrf		RC5_ShrC,w			; ToggleBit in bit 7 of RC5_Cmd
		movwf	RC5_Cmd
		movf	RC5_ShrA,w
		andlw	0x1F
		movwf	RC5_Addr
		bsf		RC5_flags,bRC5_DataReady; Only lower 5 bits of RC5_Addr are valid

lRC5_Exit
lDCF77								; DCF77 decoding stuff
		btfss	PORTA,bDCF77inp
		decf	DCF_temp,f
		btfsc	PORTA,bDCF77inp
		incf	DCF_temp,f

		decfsz	DCF_20ms_cnt,f		; Time base for DCF77 decode
		goto	lTMR2EXIT

		movlw	.100				; .100 * .200 uS = .20 ms
		movwf	DCF_20ms_cnt

		btfss	Mode,bDCFEnabled
		goto	lDCFEnd				; DCF77 bitstream decoding

;******************************************************************************
;		DCF77 decoding
;******************************************************************************
;
;Encoding Scheme:
;----------------
;
;Encoding as follows: Pulse of 0.1s = 0, 0.2s = 1
;Numbers are encoded in BCD (binary coded decimal)
;
;Mark number(s)
;
;0				Minute, always 0 (0.1s)
;
;1-14			Reserved, always 0
;
;15				Antenna (0b normal antenna, 1b backup antenna)
;
;16				Time zone change announcement, 1 hour ahead (0b nothing, 1b change)
;
;17,18			Time zone, difference in hours against UTC (mark 18 is lsb)
;				(00b = +0h, 01b = +1h = CET, 10b = +2h = CEST, 11b = +3h)
;				In the case of transmission of UTC+1, mark 18 has a duration of 0.2s and mark 17 a duration of 0.1s.
;				If UTC+2 is being transmitted, this is reversed.
;
;19				Leap second announcement. (0b nothing, 1b change)
;				The leap second is encoded in this bit one hour prior to occurrence.
;
;20				Start bit for encoded time, always 1
;
;21-27			1, 2, 4, 8, 10, 20, 40 		minutes (mark 21 is lsb)
;
;28				P1 maintains even parity for marks 21-28
;
;29-34			1, 2, 4, 8, 10, 20			hours (mark 29 is lsb)
;
;35				P2 maintains even parity for marks 29-35
;
;36-41			1, 2, 4, 8, 10, 20			day in month (mark 36 is lsb)
;
;42-44			1, 2, 4						day in week  (mark 42 is lsb)
;
;45-49			1, 2, 4, 8, 10				month number (mark 45 is lsb)
;
;50-57			1, 2, 4, 8, 10, 20, 40, 80	year (without century) (mark 50 is lsb)
;
;58				P3 maintains even parity for marks 36-58.
;				There is no marktransmitted for the 59th second.
;
;59				Normally, this bit is not transmitted (space until bit 0,minute mark).
;				It is transmitted only when there is a leap second
;				(inserted twice per 3 years, last minute of June or December).
;
;
;Announcement bit for a leap second:
;-----------------------------------
;
;The DCF77 control unit is currently being modified so that in future an
;announcement bit for a leap second can be sent. It is expected that for
;the first time on 1st July 1985 the second mark No. 19 will be extended
;to a length of 0.2s for one hour prior to the introduction of a leap second.
;Intelligent receivers will then be able to recognize the
;discontinuity and maintain correct indicated time in spite of a 61s minute.

;lDCF77Dec							; Start DCF77 decode
		incf	DCF_timer,f			; Increment 10ms timer
		btfsc	STATUS,Z			; Error if overflow
		goto	lDCFError

		clrw
		btfsc	DCF_temp,7
		movlw	1 << bDCFBit		; Get databit
		clrf	DCF_temp			; Reinit digital filter's counter
		bsf		DCF_temp,7
		xorwf	DCF_flags,w
		andlw	1 << bDCFBit		; Bit changed
		btfsc	STATUS,Z
		goto	lDCFEnd				; No -> Exit

		xorwf	DCF_flags,f			; Store new bit in DCF_flags
		btfss	DCF_flags,bDCFWaitSync
		goto	lDCFSynced

lDCFNotSynced						; Not synchronized yet
		btfsc	DCF_flags,bDCFBit
		goto	lDCFClrTimer		; Amplitude is high, measure it's length

lDCFNotSync0						; Amplitude is low
		movlw	-.150/2
		addwf	DCF_timer,w			; High period was > 1500 ms
		btfss	STATUS,C
		goto	lDCFSyncWrong		; Too short - Middle of a minute - Exit

		addlw	-.50/2
		btfss	STATUS,C			; High period was > 2000 ms
		goto	lDCFSyncFound		; No - Sync period found

									; Too long
lDCFSyncWrong
		bcf		DCF_flags,bDCFReady	; Sync too long, clear data ready
		goto	lDCFEnd

lDCFSyncFound
		movlw	~ ((1 << bDCFWaitSync) | (1 << bDCFParityErr ) | (1 << bDCFParity) | (1 << bDCFError))
		andwf	DCF_flags,f			; Yes - Start, clear partity error and data ready flags and parity bit
		clrf	DCF_bitcntr			; Init bitcounter
		decf	DCF_bitcntr,f
		goto	lDCFSynced0

lDCFSynced
		btfsc	DCF_flags,bDCFBit	; Check new bit
		goto	lDCFSync1

lDCFSynced0
		movlw	-.60/2				; High period was > 600 ms
		addwf	DCF_timer,w
		btfss	STATUS,C
		goto	lDCFError			; No - Error

		incf	DCF_bitcntr,f		; Inc bit number

lDCFClrTimer
		clrf	DCF_timer			; Clear timer
		goto	lDCFEnd

lDCFSync1
		movlw	-.30/2				; Low period was > 300 ms
		addwf	DCF_timer,w
		btfsc	STATUS,C
		goto	lDCFError			; Yes - Error

		movlw	-.15/2				; Low period was > 150 ms
		addwf	DCF_timer,w
		btfss	STATUS,C
		goto	lDCFSync1D0
		bsf		DCF_flags,bDCFData	; Set data bit
		goto	lDCFData

lDCFSync1D0
		movlw	-.6/2				; Low period was < 60 ms
		addwf	DCF_timer,w
		btfss	STATUS,C
		goto	lDCFError			; Yes - Error
		bcf		DCF_flags,bDCFData	; Clear data bit

lDCFData
		clrf	DCF_timer			; Clear timer
		bcf		DCF_flags,bDCFReady

		movlw	DCF_Year
		movwf	FSR					; FSR -> DCF_Year

		movlw	.58
		subwf	DCF_bitcntr,w
		btfsc	STATUS,Z
		goto	lDCFPar				; P3 parity bit - End of data
		addlw	.8					; >= .50
		btfsc	STATUS,C
		goto	lDCFShift			; Shift Year
		clrf	INDF				; Clear DCF_Year
		decf	FSR,f				; FSR -> DCF_Month
		addlw	.8					; >= .42
		btfsc	STATUS,C
		goto	lDCFShift			; Shift Month
		clrf	INDF				; Clear DCF_Month
		decf	FSR,f				; FSR -> DCF_Day
		addlw	.6					; >= .36
		btfsc	STATUS,C
		goto	lDCFShift			; Shift Day
		clrf	INDF				; Clear DCF_Day
		decf	FSR,f				; FSR -> DCF_Hour
		addlw	.1					; >= .35
		btfsc	STATUS,C
		goto	lDCFPar				; P2 parity bit - End of Hour
		addlw	.6					; >= .29
		btfsc	STATUS,C
		goto	lDCFShift			; Shift Hour
		clrf	INDF				; Clear DCF_Hour
		decf	FSR,f				; FSR -> DCF_Minute
		addlw	.1					; >= .28
		btfsc	STATUS,C
		goto	lDCFPar				; P1 parity bit - End of Minute
		addlw	.7					; >= .21
		btfsc	STATUS,C
		goto	lDCFShift			; Shift Minute
		clrf	INDF				; Clear DCF_Minute
		addlw	.1					; >= .20
		btfsc	STATUS,C
		goto	lDCFStart			; Start of time field - always 1
		addlw	.3					; == .17
		btfss	STATUS,Z
		goto	lDCFEnd				; Don't care leading bits

lDCFSummer							; Winter / Summer time bit
		btfsc	DCF_flags,bDCFError
		goto	lDCFEnd

		btfsc	DCF_flags,bDCFData
		bsf		DCF_flags,bDCFSummerTime
		btfss	DCF_flags,bDCFData
		bcf		DCF_flags,bDCFSummerTime
		goto	lDCFEnd

lDCFShift
		rlf		DCF_flags,w			; Shift databit to C
		rrf		INDF,f				; Shift right register

		movlw	1 << bDCFParity
		btfsc	DCF_flags,bDCFData	;If data bit is set
		xorwf	DCF_flags,f			;   modify parity bit
		goto	lDCFEnd


lDCFStart							; Start bit has to be 1
		btfss	DCF_flags,bDCFData
		goto	lDCFError
		goto	lDCFEnd

lDCFPar
		movlw	1 << bDCFParity
		btfsc	DCF_flags,bDCFData	;If data bit is set
		xorwf	DCF_flags,f			;   modify parity bit

		btfsc	DCF_flags,bDCFParity
		goto	lDCFPerror			; Parity error

		movlw	.58
		xorwf	DCF_bitcntr,w
		btfsc	STATUS,Z			; Parity 3 field
		btfsc	DCF_flags,bDCFError	; If there was no error - the decoding is completed
		goto	lDCFEnd

		incf	DCF_SNum,f			; Increment number of successfull synchronizations 8 bit only

		bsf		DCF_flags,bDCFReady	; Sign date and time ready and valid
		goto	lDCFNewSync

lDCFPerror
		bsf		DCF_flags,bDCFParityErr; parity error found
lDCFError
		bsf		DCF_flags,bDCFError	; Error in decoding
lDCFNewSync
		bsf		DCF_flags,bDCFWaitSync; Wait for new synchron
lDCFEnd

lTMR2EXIT
;--------

;--------
INT_UART

		movf	FSR,w				; Save FSR
		movwf	fsr_temp
		bsf		STATUS,IRP			; Buffers are on Bank2

		btfss	PIR1,RCIF			; Is it a receive interrupt
		goto	INT_UART_TX

		movf	UartRxChWp,w		; Put receive char to buffer if no error
		movwf	FSR
		movf	RCSTA,w				; Check for error
		andlw	(1<<FERR) | (1<<OERR)
		btfss	STATUS,Z
		goto	UART_ERR			; If error not to read char
		movf	RCREG,w				; Read character
		movwf	INDF				; Write it to buffer
		incf	FSR,w				; Move pointer in circular way
		andlw	0x0F
		iorlw	low(UartRxChar)
		movwf	UartRxChWp
		incf	UartRxChNum,f		; Sign one more char in buffer
		goto	INT_UART_TX

UART_ERR							; Receive error, reenable receive
		movf	RCREG,w				; Clear receive interrupt flag, drop character
		bcf		RCSTA,CREN			; Turn off receiver
		nop
		bsf		RCSTA,CREN			; Turn on  receiver

INT_UART_TX							; Is it a transmit interrupt
		btfss	PIR1,TXIF
		goto	INT_UART_EX

		movf	UartTxChNum,f		; Get next char from buffer if there is one
		btfsc	STATUS,Z
		goto	INT_TX_NOCHAR		; No more chars in buffer

		movf	UartTxChRp,w		; Get pointer to buffer
		movwf	FSR
		movf	INDF,w				; Get character from buffer
		movwf	TXREG				; Send it
		incf	FSR,w				; Move pointer
		andlw	0x1F				; in circular way
		iorlw	low(UartTxChar)
		movwf	UartTxChRp
		decfsz	UartTxChNum,f		; Sign one char read from buffer
		goto	INT_UART_EX			; Skip disabling transmit interrupt if more chars in buffer

INT_TX_NOCHAR						; No more chars to transmit
		bsf		STATUS,RP0
		bcf		PIE1,TXIE			; Disable transmit int
		bcf		STATUS,RP0

INT_UART_EX
		movf	fsr_temp,w			; Restore FSR
		movwf	FSR

;--------
INT_EXIT
		swapf	status_temp,w		; Restore context for main program
		movwf	STATUS				; This will also restore bank selection and IRP
		swapf	w_temp,w
		retfie						; Return from interrupt

;******************************************************************************
;	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
		movlw	.32					; DMon = 32
		movwf	DMon
		movf	Month,w				;
		movwf	Scratch				; For months <8 odd	 months have 31 days
		btfsc	Month,3
		incf	Scratch,f			; For months >7 even months have 31 days
		btfss	Scratch,0
		decf	DMon,f
		xorlw	.2					; Month = February ?
		btfss	STATUS,Z			;
		goto	lNotFeb2			; continue
		decf	DMon,f				; February has max 29 days
		movf	Year,w
		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

lNotFeb2
		movf	DMon,w
		call	CheckAndIncNext		; Check for day overflow
		btfsc	STATUS,C			; if day cleared, correct day
		incf	Day,f
									; FSR -> Month
		movlw	.13					; Check for month overflow
		call	CheckAndIncNext
		btfsc	STATUS,C			; if month cleared, correct month
		incf	Month,f

CheckYear							; FSR -> Year
		movlw	.100				;
		call	CheckAndIncNext
									; FSR -> Century
		movlw	.100				;
		subwf	Century,w			; Century < 100 ?
		btfsc	STATUS,C			;
		clrf	Century				;

		bcf		flags,bNewTime		; Clear new time flag
		btfss	Rs232Flags,bTimeSet	; If time was set by uart command
		return
		goto	StoreTime			; Write new time and date to RTC

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

;******************************************************************************
;	Check for Alarm conditions

AlarmCheck
		clrf	Scratch				; Get mask of daw of week
		incf	WDay,w				; Wday code is 0..6
		movwf	Scratch2
		bsf		STATUS,C			; Set C
Alarm_Wday
		rlf		Scratch,f			; Clears C
		decfsz	Scratch2,f
		goto	Alarm_Wday

Alarm_loop_init						; Scratch has now a 1 on bit number Wday
		movlw	Alarm0WDay			; Pointer to alarm table
		movwf	FSR

Alarm_loop
		btfss	INDF,7				; Check alarm enable flag
		goto	Alarm_dis			; Disabled

		movf	INDF,w				; Get alarm day of week flags
		andwf	Scratch,w			; Mask with actual day of week mask
		btfsc	STATUS,Z
		goto	Alarm_dis			; Not enabled for actual day of week

		incf	FSR,f				; Move pointer to alarm hour
		movf	Hour,w				; Get actual hour
		xorwf	INDF,w				; Compare with alarm hour
		movwf	Scratch2			; Store result

		btfsc	INDF,5				; If alarm hour >= 32, hour masked out from comparation
		clrf	Scratch2

		incf	FSR,f				; Move pointer to alarm minute
		movf	Minute,w			; Get actual minute
		xorwf	INDF,w				; Compare with alarm minute
		iorwf	Scratch2,w			; Combine with hour comp. result
		btfss	STATUS,Z
		goto	Alarm_dis			; Not Z, no alarm

		incf	FSR,f				; Move pointer to alarm minute
		btfsc	INDF,7				; Test for alarm mode
		goto	AlarmSwitch			; Goto if switch

		btfsc	INDF,6				; Test for propeller start
		call	NotStandbyPb		; Start up propeller
		btfsc	INDF,5				; Test for propeller stop
		call	ToStandby			; Stop propeller
		btfss	INDF,4				; Test for sound enabled
		goto	Alarm_dis

		movf	INDF,w
		andlw	0x0F
		btfsc	STATUS,Z
		movlw	.8					; Init sound counter
		movwf	SoundCnt
		goto	Alarm_dis

AlarmSwitch
		movf	INDF,w				; Get turn on time (minutes)
		andlw	0x7F
		movwf	SwitchCnt			; Set up counter
		btfss	STATUS,Z
		bsf		PORTB,bRelais		; Turn on  relais
		btfsc	STATUS,Z
		bcf		PORTB,bRelais		; Turn off relais

Alarm_dis							; Move to next alarm setting
		bsf		FSR,0
		bsf		FSR,1
		incf	FSR,f
		movlw	0xF0				; Check for end of table
		xorwf	FSR,w
		btfss	STATUS,Z
		goto	Alarm_loop			; Loop for all alarm entries

		btfss	Alarms,5			; Check for controllers with mode RAM
		goto	Alarm_loop_ex

		btfsc	STATUS,IRP			; Bank3 was used ?
		goto	Alarm_loop_ex		; End of alarm table
		bsf		STATUS,IRP			; Use Bank3
		goto	Alarm_loop_init
Alarm_loop_ex
		bcf		STATUS,IRP			; IRP = 0
		return

;******************************************************************************
;	Process RC5 commands

ProcessRC5
		movlw	'A'					; RC5 packet info will be sent
		call	UART_SEND
		movf	RC5_Addr,w			; Send address
		call	UART_SEND_BYTE
		movf	RC5_Cmd,w			; Send command
		call	UART_SEND_BYTE_CR

	ifdef	RC5AddrCheck
		movf	RC5_Addr,w			;
		xorlw	RemoteAddr			; test if RC5_Addr = RemoteAddr
		btfss	STATUS,Z			;
		goto	ProcessRC5Done		;
	endif

		movf	RC5_Cmd,w			; Get command code
		xorwf	RC5_Cmd2,f			; Compare with previous one
		btfsc	STATUS,Z			;
		goto	ProcessRC5Done		; Exit if the same code

		andlw	0x7F				; Mask toggle bit

		select_w
		case	WIDTH_DN			;
		  goto	DecWidth			; Decrement pulse width frequency
		case	WIDTH_UP			;
		  goto	IncWidth			; Increment pulse width frequency
		case	POWUPCOIL			;
		  goto	PowerUpCoil			; Power up coil
		case	POWDNCOIL			;
		  goto	PowerDownCoil		; Power down coil

		case	TINDEX
		  goto	ToggleIndex			; Toggle index LED
		case	ALM_SND
		  clrf	SoundCnt			; Alarm sound off
		case	REL_ON				;
		  goto	RelaisOn			; Turn relais on
		case	REL_OFF				;
		  goto	RelaisOff			; Turn relais off
		case	STANDBY				;
		  goto	ToggleStndBy		; StandBy
;		default
		call	ReloadASTimer		; Reload timer if valid command sent to clock

		goto	ProcessRC5Done		;

RelaisOn
		bsf		PORTB,bRelais		; Turn on relais output
		movf	RelOnIr,w			; Get turn on time
		goto	LoadSwitchCnt

RelaisOff							; Turn off relais output
		bcf		PORTB,bRelais
LoadSwitchCnt
		movwf	SwitchCnt
		goto	ProcessRC5Done		;

ToggleIndex							; Toggle index led
		movlw	1 << bIndexLed		;
		xorwf	PORTB,f				;
		movlw	1 << bIndexOn		; Toggle in flags too
		xorwf	flags,f
		goto	ProcessRC5Done		;

IncWidth							; Increment pulse width
		decf	CCPR1L,f
		goto	SetWidth

DecWidth							; Decrement pulse width
		incf	CCPR1L,f
SetWidth
		movlw	RTC_PWMData			; Use run address
		btfsc	flags,bCoilPower	; Coil powered up?
		addlw	0x02				; No - Use sleep addresses
		call	SetWordAdr
		movlw	CCP1_Run			; Update in memory	use run variable
		btfsc	flags,bCoilPower	; Coil powered up?
		movlw	CCP1_Sleep			; No - Use sleep variable
		movwf	FSR
		movf	CCPR1L,w			; Get pulse width

IndfToRTC							; Write to INDF and send on I2C
		movwf	INDF
		call	I2CByteWrite
		goto	ProcessRC5Done		;

PowerUpCoil							; High power drive to trafo coil
		call	InitPWMHighPower	;
		goto	ProcessRC5Done		;

PowerDownCoil						; Low power drive to trafo coil
		call	InitPWMLowPower		;
		goto	ProcessRC5Done		;

ToggleStndBy						; Toggle mode
		call	TgStandBy
;		goto	ProcessRC5Done		;

ProcessRC5Done
		movf	RC5_Cmd,w			;
		movwf	RC5_Cmd2			; Store new command code
		bcf		RC5_flags,bRC5_DataReady;
		return						;

;******************************************************************************
;	Init variables from RTC

InitData
		call	InitTime			; Get time from RTC
AlmInit
		movlw	Alarm0WDay			; Pointer to Alarm table
		movwf	FSR
AlmClear							; Clear all Alarm
		clrf	INDF
		incf	FSR,f
		movlw	0xF0
		xorwf	FSR,w
		btfss	STATUS,Z
		goto	AlmClear

		btfss	Alarms,5			; Check for controllers for more RAM
		goto	AlmInitEx
		btfsc	STATUS,IRP			; Bank3 was used ?
		goto	AlmInitEx			; End of alarm table
		bsf		STATUS,IRP			; Use Bank3
		goto	AlmInit
AlmInitEx
		bcf		STATUS,IRP

		btfsc	flags,bTimeInv		; If time read from RTC is invalid
		return

		movlw	RTC_AlarmData		; Address of alarm data in RTC
		call	SetWordAdr

AlmLdInit
		movlw	Alarm0WDay			; Read Alarm table
		movwf	FSR					; Point to Alarm table
AlmLd
		call	I2CByteRead			; Read alarm data from RTC
		movwf	INDF
		incf	FSR,f
		movlw	0xF0
		xorwf	FSR,w
		btfss	STATUS,Z			; Loop for all alarm records
		goto	AlmLd

		btfss	Alarms,5			; Check for controllers wiht more RAM
		goto	Alarm_loop_ex		; End of alarm table , set IRP = 0
		btfsc	STATUS,IRP			; Bank3 was used ?
		goto	Alarm_loop_ex		; End of alarm table , set IRP = 0
		bsf		STATUS,IRP			; Use Bank3
		goto	AlmLdInit

;******************************************************************************
;	Init PWM

InitPWM
		movlw	b'00100101'			;
		movwf	T2CON				; Timer2=ON, Prescaler = 1/4, postscaler = 1 / 5
		movlw	0x0C				; Set CCP in PWM mode
		movwf	CCP1CON				;

; Low power

InitPWMLowPower
		bsf		STATUS,RP0			; Bank1
		btfsc	Mode,bDeapSleep		; If deap sleep enabled
		bsf		TRISB,bTrafo		; Disable trafo FET
		btfss	Mode,bDeapSleep		; If deap sleep disabled
		bcf		TRISB,bTrafo		; Enable  trafo FET
		bcf		STATUS,RP0			; Bank0
		movf	CCP1_Sleep,w		; Set dutycycle for sleep mode
		movwf	CCPR1L				;
		bcf		flags,bCoilPower
		return

;--------
; Wake up from sleep / standby

NotStandbyPb
		bcf		PORTB,bIndexLed		; turn on index led
		bsf		flags,bIndexOn
		bcf		flags,bStandBy		; spin motor
		bcf		flags,bSleeping		; stop sleeping
	#ifdef	InvertSpeedControl
		bsf		PORTB,bMotorOFF		; startup
	#else
		bcf		PORTB,bMotorOFF		; startup MIC 2940A
	#endif
		movf	Speed_Sleep,w
		btfss	flags,bLowSpeed
		movf	Speed_Run,w
		movwf	Speed

; High power

InitPWMHighPower
		bsf		STATUS,RP0			; Bank1
		bcf		TRISB,bTrafo		; Enable trafo FET
		bcf		STATUS,RP0			; Bank0
		movf	CCP1_Run,w			;
		movwf	CCPR1L				; CCPR1L = Run
		bsf		flags,bCoilPower

; Reload Auto standby timer

ReloadASTimer
		movf	AutoStbyTimH,w		; Count is in seconds
		movwf	StandbyTimerH
		movf	AutoStbyTimL,w
		movwf	StandbyTimerL
		return

TgStandBy
		btfsc	flags,bStandBy		;
		goto	NotStandbyPb		;

;--------
; Go to standby

ToStandby							; Go to standby
		movlw	.5					;
		movwf	SleepTimer			; SleepTimer = 5 seconds

		bsf		PORTB,bIndexLed		; turn off index led
		bcf		flags,bIndexOn
		bsf		flags,bStandBy		; motor in standby
	#ifdef	InvertSpeedControl
		bcf		PORTB,bMotorOFF		; shutdown
	#else
		bsf		PORTB,bMotorOFF		; shutdown MIC 2940A
	#endif
		clrf	Speed

ClearASTimer
		clrf	StandbyTimerH
		clrf	StandbyTimerL
		return

;-------
;Returns C=1 if sound enabled - Daytime speed

ChkSoundTime
		movf	HourSndBeg,w		; If after begin hour
		subwf	Hour,w
		btfss	STATUS,C
		return
		incf	Hour,w				; If before end hour
		subwf	HourSndEnd,w
		return

;**********************************************************************
; Initialization

main
InitIO
;;;		clrf	PORTA				; Clear output for I2C SCL & SDA
;;;		clrf	PORTB				;
;;;		movlw	(1 << bLED)|(1 << bIndexLed)|(1 << bMotorOFF); Done at reset

		movwf	PORTB
		bsf		STATUS,RP0
		movlw	0xFF				; Set PORTA 0..7 as input
		movwf	TRISA				; I2C routines turn on bSCL and bSDA output if needed
		movlw	0xFF ^ ( (1 << bLED) | (1 << bRelais) | (1 << bSoundOut) | (1 << bMotorOFF) | (1 << bIndexLed) )
		movwf	TRISB				; Disable trafo drive

		clrf	PIE1				; dissable all possible interrupt flag
		movlw	b'10000001'			; set up timer0, prescaler(bit3)bypassed
		movwf	OPTION_REG			; send w to option.

		bsf		PIE1,TMR2IE			; Enable Peripheral interrupt from TMR2
		movlw	.49					; set frequency for 40uS interrupt
		movwf	PR2					;

		bsf		STATUS,RP1			; Bank3
		movlw	.20
		movwf	Rs232Temp			; Detect controller type
		addwf	Rs232Temp,w			; 16F87, 16F88 will read .20, others 0

		clrf	STATUS				; Bank0, IRP=0
		movwf	Alarms				; Alarms,5 == 1 if Bank3 usable

		movlw	0x9B				; Address of ANSEL
		movwf	FSR

		btfsc	Alarms,5			; On 16F87, 16F88
		clrf	INDF				; Use all PORTA pins as digital I/O, A/D is off afther reset

		movlw	0x07				;
		btfss	Alarms,5			; On 16F628(A), 16F648A
		movwf	0x1F				; CMCON - Turn comparators OFF, use all PORTA pins as digital I/O

; DCF77 v
		clrf	DCF_SNum			; Clear counter of successfull synchronizations
		clrf	DCF_PNum
		clrf	DCF_temp
		bsf		DCF_temp,7
		movlw	.100				; .100 * .200 uS = .20 ms
		movwf	DCF_20ms_cnt
		bsf		DCF_flags,bDCFWaitSync; DCF is waiting for synchron
; DCF77 ^
		call	InitSubSecCnt

		clrf	flags2
		movf	PORTA,w
		xorlw	(1 << bSCL) | (1 << bSDA)
		andlw	(1 << bSCL) | (1 << bSDA)
		btfss	STATUS,Z
		bsf		flags2,bI2CBusError

		clrf	SpeedSheed

		movlw	0xff
		movwf	WuBtCnt
		movwf	RyBtCnt
		movwf	LastMinute

		clrf	SoundCnt			; Clear down counter for alarm sound
		clrf	SwitchCnt			; Clear down counter for relais controll

		clrf	Rs232Flags			; Clear uart's command decode flags
		clrf	RC5_flags			; clear flags
		clrf	RC5_Tmr				;
		bsf		RC5_flags,bRC5_WaitStart; RC5 is waiting for startbit

		movlw	(1 << bStandBy)|(1 << bSleeping)
		movwf	flags				; Init flags: Sleeping

		call	InitData			; Init data from RTC

		call	UART_INIT			; Init uart and it's buffers

		call	InitPWM				; Init the low power PWM output to drive primary coil

		clrf	SleepTimer			; SleepTimer = 0 seconds
		call	ClearASTimer		; Clear auto standby timer

		btfsc	Mode,bWakeRun		; Wake up in run mode
		call	NotStandbyPb		; Start in Active mode

SendMessage
		movlw	(1 << GIE) | (1 << PEIE); Enable global, extended peripheral interrupt
		movwf	INTCON

		call	SEND_MESSAGE		; Send message

;================================================================================
;	Main loop

MainLoop
		btfsc	RC5_flags,bRC5_DataReady; Decode Command received from RC5 receiver
		call	ProcessRC5			;

		movf	DCF_flags,w			; If DCF ready and not error and start of new frame
		andlw	(1 << bDCFReady) | (1 << bDCFParityErr) | (1 << bDCFError) | (1 << bDCFWaitSync)
		xorlw	(1 << bDCFReady)
		btfsc	STATUS,Z			; If DCF decoded data available and no error
		call	DCFCopy				; Copy time to clock's registers

		call	UARTCmdDec			; Decode Command received from UART

		btfss	flags,bNewTime		; test new time flag
		goto	TestButton

		call	TimeCheck			; half second past, do TimeCheck
		call	CalcWDay
		movwf	WDay				; Calculate daz of week

		movf	Second2,f
		btfss	STATUS,Z			; Minute passed
		goto	TestButton

		movf	LastMinute,w
		xorwf	Minute,w			; Check this minute processed
		btfsc	STATUS,Z			; DCF77 synchronization can move time backward
		goto	TestButton

		xorwf	LastMinute,f
		call	AlarmCheck			; Check for alarm condition

;ChkHourSnd
		movf	Minute,w
		btfss	STATUS,Z			; At hour change
		goto	TestButton

		movf	DCF_SNum,w
		movwf	DCF_PNum
		clrf	DCF_SNum

		decf	Hour,w
		movwf	I2CWordAdr
		movlw	.23
		btfsc	I2CWordAdr,7
		addwf	I2CWordAdr,f
		movlw	RTC_DCFData
		addwf	I2CWordAdr,f
		movf	DCF_PNum,w
		call	I2CByteWrite

		btfss	Mode,bHourSign		; If hour sound enabled
		goto	CheckMidnight

		call	ChkSoundTime
		btfss	STATUS,C
		goto	CheckMidnight

		movf	SoundCnt,f			; If no alarm pending
		btfss	STATUS,Z
		goto	CheckMidnight

		movlw	1					; Set only one sound
		btfss	Mode,bHourSignMode	; If hour mode
		goto	EnHourSound

		movlw	.12					; If hour >=12
		subwf	Hour,w				; W = Hour - 12
		btfss	STATUS,C
		movf	Hour,w
		btfsc	STATUS,Z			; If W ==0
		addlw	.12					; W = W+12

EnHourSound
		movwf	SoundCnt			; Set sound count

CheckMidnight
		movf	Hour,w				; At midnight
		btfss	STATUS,Z
		goto	TestButton

		btfsc	Mode,bReinitTime	; If enabled
		call	InitTime			; Reinit time from RTC

TestButton
		btfss	Mode2,bMotorModeEn
		goto	MotorSpeedEnd

		btfss	Mode2,bMotorModeMan
		goto	MotorSpeedAuto

		bsf		STATUS,C
		btfsc	Mode2,bLowSpeed
		bcf		STATUS,C
		goto	MotorSpeedSet

MotorSpeedAuto
		call	ChkSoundTime
		btfss	STATUS,C
		bcf		flags2,bNight
		btfsc	STATUS,C
		bsf		flags2,bNight

MotorSpeedSet
		btfsc	STATUS,C
		goto	HighSpeed
		movf	Speed_Sleep,w
		movwf	Speed
		bsf		flags2,bLowSpeed
		goto	MotorSpeedEnd

HighSpeed
		movf	Speed_Run,w
		movwf	Speed
		bcf		flags2,bLowSpeed

MotorSpeedEnd

		movf	PORTA,w
		movwf	Scratch

		movlw	0xFF
		btfsc	Scratch,bButton		; Wake up button pressed
		movwf	WuBtCnt
		btfsc	Scratch,bButton		; Wake up button pressed
		goto	WuDone
		movf	WuBtCnt,f
		btfss	STATUS,Z
		decfsz	WuBtCnt,f
		goto	WuDone
		bsf		flags,bButtonDn
WuDone
		movlw	0xFF
		btfsc	Scratch,bButton2	; Relais button pressed
		movwf	RyBtCnt
		btfsc	Scratch,bButton2	; Relais button pressed
		goto	RyDone
		movf	RyBtCnt,f
		btfss	STATUS,Z
		decfsz	RyBtCnt,f
		goto	RyDone
		bsf		flags,bButton2Dn
RyDone

		btfss	flags,bButtonDn		; Button pressed
		goto	TestRyButton
		bcf		flags,bButton2Dn

		movf	SoundCnt,f
		btfss	STATUS,Z
		goto	CancelSoundWu

		call	TgStandBy
		bcf		flags,bButtonDn
		goto	TestRyButton

CancelSoundWu
		clrf	SoundCnt

TestRyButton
		btfss	flags,bButton2Dn	; Realis button pressed
		goto	TestSleep

		bcf		flags,bButton2Dn
		movf	SoundCnt,f
		btfss	STATUS,Z
		goto	CancelSoundRy

		movf	RelOnIr,w
		movf	SwitchCnt,f
		btfss	STATUS,Z
		clrw
		movwf	SwitchCnt			; Set time
		bcf		PORTB,bRelais
		movf	SwitchCnt,f
		btfss	STATUS,Z
		bsf		PORTB,bRelais
		goto	TestSleep

CancelSoundRy
		clrf	SoundCnt

TestSleep
		btfsc	flags,bSleeping		; yes, in standby: already sleeping?
		goto	MainLoop			; yes, sleeping, continue main loop

		btfsc	flags,bStandBy		; in standby?
		goto	CheckToSleep		; no, continue main loop

		movf	StandbyTimerH,w		; Check for StandbyTimer(16 bit)==0
		iorwf	StandbyTimerL,w
		btfss	STATUS,Z
		goto	MainLoop

		btfsc	Mode,bAutoStby		; If AutoStandby enabled
		call	ToStandby			; Go to standby
		goto	MainLoop

CheckToSleep
		movf	SleepTimer,w		;
		btfss	STATUS,Z			; SleepTimer == 0
		goto	MainLoop			; no, continue main loop

		call	InitPWMLowPower		; Low power drive to trafo coil

		bsf		flags,bSleeping		; Sleeping
		goto	MainLoop			;

;******************************************************************************

InitSubSecCnt
		movlw	0x3C				; load counter SubSecond = 0x10000 - .2500 = 0xF63C
		movwf	SubSec_L			;
		movlw	0xF6				;
		movwf	SubSec_H			;
		return

;******************************************************************************
;		DCF77 synchronizing - copy DCF's date and time to clock's variables
;******************************************************************************

DCFDayHour
		decf	FSR,f				; Move to next DCF data
		rrf		INDF,w				; Get data >> 1
		movwf	Scratch
		rrf		Scratch,w			; Get data >> 2

;******************************************************************************
; Convert BCD number to binary - mask bit 7..6 off

BcdToBin3F							; Convert BCD to binary for Hour and Day
		andlw	0x3F

; Convert BCD number to binary

BcdToBin							; Convert BCD to binary
		movwf	BCD
		andlw	0x0F
		btfsc	BCD,4
		addlw	.10
		btfsc	BCD,5
		addlw	.20
		btfsc	BCD,6
		addlw	.40
		btfsc	BCD,7
		addlw	.80
		return

;******************************************************************************
; Decode and copy DCF time to

DCFCopy
		movlw	DCF_Year			; Time variable are in Bank0, DCF are in Bank1
		movwf	FSR					; Indirect access to DCF time variables
									; FSR -> DCF_Year
		movf	INDF,w				; Get DCF Year
		call	BcdToBin			; DCF77 uses BCD encoding
		xorwf	Year,w
		movwf	Scratch2			; Init change flag
		xorwf	Year,f
		decf	FSR,f				; FSR -> DCF_Month
		rrf		INDF,w				; Get DCF Month
		movwf	Scratch				; Shift month to bit0
		rrf		Scratch,f
		rrf		Scratch,w
		andlw	0x1F				; Keep month only
		call	BcdToBin			; DCF77 uses BCD encoding
		xorwf	Month,w
		iorwf	Scratch2,f			; Modify change flag
		xorwf	Month,f
		call	DCFDayHour			; Get DCF Day
									; FSR -> DCF_Day
		xorwf	Day,w
		iorwf	Scratch2,f			; Modify change flag
		xorwf	Day,f
		call	DCFDayHour			; Get DCF Hour
									; FSR -> DCF_Hour
		xorwf	Hour,w
		iorwf	Scratch2,f			; Modify change flag
		xorwf	Hour,f
		decf	FSR,f				; FSR -> DCF_Minute
		rrf		INDF,w				; Get DCF Minute
		andlw	0x7F
		call	BcdToBin			; DCF77 uses BCD encoding
		xorwf	Minute,w
		iorwf	Scratch2,f			; Modify change flag
		xorwf	Minute,f
		clrc
		rrf		Second2,w
		iorwf	Scratch2,f			; Modify change flag
		clrf	Second2				; Seconds are always 0

		bcf		DCF_flags,bDCFReady	; DCF data processed, Clear DCF data ready

		call	InitSubSecCnt		; Synchronize subsec counter

		movf	Scratch2,f
		btfsc	STATUS,Z
		return

		bsf		flags,bNewTime		; Set new time ready if different time was received
		bsf		Rs232Flags,bTimeSet	; Sign time has to be saved
		return

;******************************************************************************
;	UART functions
;******************************************************************************

;******************************************************************************
; Command decoding

UARTCmdDec
		movlw	high(CmdParNum)
		movwf	PCLATH

		btfsc	Rs232Flags,bCmdExec	; If command execution in progress
		goto	GetParam			; Get parameters of command

GetCommand
		call	UART_RECEIVE		; Get the command character
		btfsc	STATUS,C			; Was there available character
		return						; Return if not
		addlw	-'`'				; Check the range '`', 'a'...'z'
		btfss	STATUS,C
		return
		movwf	Rs232Cmd			; Save code of command
		addlw	-(0x7F+1-'`')
		btfsc	STATUS,C
		return
		movf	Rs232Cmd,w			; Get code of command
		call	CmdParNum			; Get number of parameters
		movwf	Rs232ParNum
		bsf		Rs232Flags,bCmdExec	; Sign command execution in progress

GetParam
		movf	Rs232ParNum,w		; Are there enought parameters in buffer
		subwf	UartRxChNum,w
		btfss	STATUS,C
		return						; Return if not

CmdParOk
		movf	Rs232Cmd,w			; Get command code
		bcf		Rs232Flags,bCmdExec	; No command execution in progress

		goto	CmdExec				; Execute it and return to caller at the end of execution

;**********************************************************************
; Init uart

UART_INIT:
		clrf	UartRxChNum			; Init receive fifo
		movlw	low(UartRxChar)
		movwf	UartRxChWp
		movwf	UartRxChRp

		clrf	UartTxChNum			; Init transmit fifo
		movlw	low(UartTxChar)
		movwf	UartTxChWp
		movwf	UartTxChRp

		bsf		STATUS,RP0			; Bank1

	if	BaudRate<.9600				; Set up Baud rate
		bcf		TXSTA,BRGH
		movlw	(F_OSC/.64/BaudRate)-1
	else
		bsf		TXSTA,BRGH
		movlw	(F_OSC/.16/BaudRate)-1
	endif

		movwf	SPBRG
		bsf		TXSTA,TXEN			; Enable transmission

		bsf		PIE1,RCIE			; Enable  receive interrupt
		bcf		PIE1,TXIE			; Disable transmit interrupt

		bcf		STATUS,RP0			; Bank0

		bsf		RCSTA,SPEN			; Enable receive
		bsf		RCSTA,CREN

		return

;**********************************************************************
; Send a byte in BCD format with uart as two ASCII character
; Keeps IRP, RP1, RP0 bits of STATUS
; Keeps FSR

UART_SEND_BCD
		call	Conv2BCD

;**********************************************************************
; Send a byte with uart as two hex. ASCII character
; Keeps IRP, RP1, RP0 bits of STATUS
; Keeps FSR

UART_SEND_BYTE
		movwf	Scratch				; Save data to send
		swapf	Scratch,w			; Send high nibble first
		call	UART_SEND_NIBBLE
		movf	Scratch,w			; Send low	nibble

;**********************************************************************
; Send a nibble (lower 4 bit of W) with uart as a hex. ASCII character
; Keeps IRP, RP1, RP0 bits of STATUS
; Keeps FSR

UART_SEND_NIBBLE
		andlw	0x0F				; Keep only low nibble
		movwf	Rs232Temp2
		sublw	0x09
		clrw
		btfss	STATUS,C			; If nibble > 9 add 7, and use lower case character
		movlw	0x27
		addwf	Rs232Temp2,w
		addlw	0x30				; Convert to ACSII

;**********************************************************************
; Send a character (w), write it to uart send buffer
; Keeps IRP, RP1, RP0, DC, C bits of STATUS
; Keeps FSR

UART_SEND							; Sends char in w

		btfsc	UartTxChNum,5		; Test number of chars in buff
		goto	UART_SEND

		movwf	Rs232Temp			; Save character to send
		swapf	STATUS,w
		movwf	Rs232Status			; Save Status
		movf	FSR,w				; Save FSR
		movwf	Rs232Fsr

		bsf		STATUS,RP0			; Bank1
		bcf		PIE1,TXIE			; Disable TX interrupt
		bcf		STATUS,RP0			; Bank0

		bsf		STATUS,IRP			; Buffer in on Bank2

		movf	UartTxChWp,w		; Get write pointer
		movwf	FSR

		movf	Rs232Temp,w			; Get char to send
		movwf	INDF				; Store it in out buffer
		incf	FSR,w				; Move the pointer
		andlw	0x1F				; in circular way
		iorlw	low(UartTxChar)
		movwf	UartTxChWp
		incf	UartTxChNum,f		; Increment number of available characters

		bsf		STATUS,RP0			; Bank1
		bsf		PIE1,TXIE			; Enable TX interrupt

UART_Exit
		bcf		STATUS,RP0			; Bank0
		movf	Rs232Fsr,w			; Restore FSR
		movwf	FSR
		swapf	Rs232Status,w		; Restore STATUS
		movwf	STATUS				; Restore IRP, Bank selection
		movf	Rs232Temp,w			; Get char sent / just received
		return

;**********************************************************************
; Get a byte from receive buffer and convert it form BCD to bin
; Keeps IRP, RP1, RP0 bits of STATUS
; Keeps FSR

UART_REC_BCD
		call	UART_REC_BYTE
		goto	BcdToBin

;**********************************************************************
; Get a byte from receive buffer
; Keeps IRP, RP1, RP0 bits of STATUS
; Keeps FSR

UART_REC_BYTE
		clrf	Rs232Temp2			; Clear data to be read
		call	UART_REC_NIBBLE		; Receive high nibble
		movwf	Rs232Temp2
		swapf	Rs232Temp2,f		; Swap to high nibble

UART_REC_NIBBLE
		call	UART_RECEIVE
		btfsc	Rs232Temp,6			; Check for 'A'..'F' or 'a'..'f'
		addlw	-7
		andlw	0x0F				; Keep low nibble
		addwf	Rs232Temp2,w
		return

;**********************************************************************
; Get a character from receive buffer
; Keeps IRP, RP1, RP0, DC, C bits of STATUS
; Keeps FSR

UART_RECEIVE						; Gets char into w, C == 1 if no char was in fifo

		bsf		STATUS,C			; Set no character received
		swapf	STATUS,w			; Save STATUS
		movwf	Rs232Status
		movf	FSR,w				; Save FSR
		movwf	Rs232Fsr

		bsf		STATUS,IRP			; Buffer in on Bank2

		bsf		STATUS,RP0			; Bank1
		bcf		PIE1,RCIE			; Disable RC interrupt
		bcf		STATUS,RP0			; Bank0

		clrf	Rs232Temp
		movf	UartRxChNum,w		; Check number of available chars
		btfsc	STATUS,Z
		goto	NO_REC_CHAR			; Jump if no character available

		movf	UartRxChRp,w		; Get read pointer
		movwf	FSR

		movf	INDF,w				; Read it from buffer
		movwf	Rs232Temp
		incf	FSR,w				; Move the pointer
		andlw	0x0F				; in circular way
		iorlw	low(UartRxChar)
		movwf	UartRxChRp
		decf	UartRxChNum,f		; Decrement number of available characters
		bcf		Rs232Status,C+4		; Valid character received - !Status was swapped!

NO_REC_CHAR
		bsf		STATUS,RP0			; Bank1
		bsf		PIE1,RCIE			; Enable RC interrupt

		goto	UART_Exit			; Restore STATUS and FSR

;**********************************************************************
; Send init message

SEND_MESSAGE
		movlw	high(InitMsg)		; Init string pointer high part
		movwf	Rs232StrPtrH
		movlw	low(InitMsg)		; Init string pointer low  part

;**********************************************************************
; Send a string (high part of address must be in Rs232StrPtrH)

SEND_STRING
		movwf	Rs232StrPtr			; Save string pointer low  part
lString0
		call	MsgTabl				; Get next character
		movwf	Rs232Temp2			; Save it
		andlw	0x7F				; Mask bit7 off
		call	UART_SEND			; Send character
		btfss	Rs232Temp2,7		; Check for end of string mark
		goto	lString0			; Loop for all characters
		return

;******************************************************************************
;	I2C RTC functions
;******************************************************************************

;******************************************************************************
; Read the time from RTC

InitTime							; Init time from I2C RTC
		call	InitSubSecCnt
		clrf	Second2

		movlw	RTC_Tenms			; Address of 1/100 Seconds
		call	SetWordAdr
									; I2CWordAdr = 0x01
		call	I2CByteRead			; Read 1/100 seconds
		addlw	-0x50
		btfsc	STATUS,C
		bsf		Second2,0			; Set half seconds
									; I2CWordAdr = 0x02

		call	I2CByteRead			; Read seconds
		call	BcdToBin
		addwf	Second2,f
		addwf	Second2,f
									; I2CWordAdr = 0x03

		call	I2CByteRead			; Read minute
		call	BcdToBin
		movwf	Minute
									; I2CWordAdr = 0x04
		call	I2CByteRead			; Read hour
		call	BcdToBin3F			; Mask format and AM/PM
		movwf	Hour
									; I2CWordAdr = 0x05
		call	I2CByteRead			; Read day
		call	BcdToBin3F			; Mask Year bits
		movwf	Day
		swapf	Scratch,f
		rrf		Scratch,f
		rrf		Scratch,w
		andlw	0x03
		movwf	Scratch2			; Save year bit 1..0
									; I2CWordAdr = 0x06
		call	I2CByteRead			; Read Month
		andlw	0x1F
		call	BcdToBin
		movwf	Month
									; I2CWordAdr = 0x07

		movlw	RTC_Year			; Address of Year
		call	I2CByteReadStoreAddr; Read year
									; I2CWordAdr = 0x11
		movwf	Year
		andlw	0x03
		movwf	Scratch3
		call	I2CByteRead			; Read year ^ 0xff
									; I2CWordAdr = 0x12
		xorlw	0xFF				; Complement read data
		xorwf	Year,w				; Has to be same as Year
		btfss	STATUS,Z
		goto	InitTimeDef			; Year not valid, init with default values

		call	I2CByteRead			; CCP1_Run
		movwf	CCP1_Run
									; I2CWordAdr = 0x13
		call	I2CByteRead			; PR2_Run
		movwf	Speed_Run
									; I2CWordAdr = 0x14
		call	I2CByteRead			; CCP1_Sleep
		movwf	CCP1_Sleep
									; I2CWordAdr = 0x15
		call	I2CByteRead			; PR2_Sleep
		movwf	Speed_Sleep
									; I2CWordAdr = 0x16
		call	I2CByteRead			; Auto Standby Time high
		movwf	AutoStbyTimH		;
									; I2CWordAdr = 0x17
		call	I2CByteRead			; Auto Standby Time	 low
		movwf	AutoStbyTimL		;
									; I2CWordAdr = 0x18

		call	I2CByteRead			; Hour sound begin
		movwf	HourSndBeg			;
									; I2CWordAdr = 0x19

		call	I2CByteRead			; Hour sound end
		movwf	HourSndEnd			;
									; I2CWordAdr = 0x1A

		call	I2CByteRead			; Read Century
		movwf	Century
									; I2CWordAdr = 0x1B

		call	I2CByteRead			; Read Relais on time for ir commands
		movwf	RelOnIr
									; I2CWordAdr = 0x1C

		movlw	RTC_Mode
		call	I2CByteReadStoreAddr; Mode
		movwf	Mode				;
									; I2CWordAdr = 0x1F

		call	I2CByteRead			; Mode2
		movwf	Mode2				;
									; I2CWordAdr = 0x20

		bcf		flags,bNewTime		; Clear new time flag

		movf	Scratch3,w			; Has the RTC increment Year
		subwf	Scratch2,w			; W = (RTC_Year % 4) - (Year % 4)
		bcf		Scratch2,0
		movwf	Scratch3
		btfsc	Scratch3,7
		addlw	4
		btfss	STATUS,Z
		bsf		Scratch2,0
		addwf	Year,f				; Yes, increment Year

		movlw	Year				; Prepare to check Year and Century
		movwf	FSR					; Will correct year and century for leap year
		call	CheckYear
		call	TimeCheck

		btfss	Scratch2,0
		return

		bsf		flags,bNewTime		;  new time flag
		bsf		Rs232Flags,bTimeSet	;
		return

;******************************************************************************
; Init time with default value: 2001-01-01 12:00:00, day of week: 1 ,set time invalid
; Init CCPR1L, RP2 for Run and Sleep mode with default values
; Init Auto Standby Timer with default value
; Init mode with default value
; Write default data to RTC

InitTimeDef
;;		clrf	Hour				; May start from 00:00:00
		movlw	.12					; why do clocks always start
		movwf	Hour				; at 12:00 ?
;;
		clrf	Minute
		clrf	Second2
		movlw	.1
		movwf	Day
		clrf	WDay
		movwf	Month
		movwf	Year

		bsf		flags,bTimeInv		; Sign time not valid

									; Write default data to RTC
		movlw	RTC_PWMData			; Set word address
		call	SetWordAdr			; Set RTC's slave address

									; PWM parameters
		movlw	CCPR1LRUN			; Set pulse width for run mode
		movwf	CCP1_Run			;
		call	I2CByteWrite		;

		movlw	SPEEDRUN			; Set speed for high speed mode
		movwf	Speed_Run			;
		call	I2CByteWrite

		movlw	CCPR1LSleep			; Set pulse width for sleep mode
		movwf	CCP1_Sleep
		call	I2CByteWrite

		movlw	SPEEDSLEEP			; set speed for low sleep mode
		movwf	Speed_Sleep			;
		call	I2CByteWrite

		movlw	high(.3600)			; Auto Standby time
		movwf	AutoStbyTimH		;
		call	I2CByteWrite
		movlw	low(.3600)			; Auto Standby Timer
		movwf	AutoStbyTimL		;
		call	I2CByteWrite

		movlw	.8
		movwf	HourSndBeg			; Hour sound begin hour
		call	I2CByteWrite

		movlw	.20
		movwf	HourSndEnd			; Hour sound end   hour
		call	I2CByteWrite

		movlw	.20
		movwf	Century				; Century
		call	I2CByteWrite

		movlw	0xFF				; Relais on time for ir commands
		movwf	RelOnIr
		call	I2CByteWrite

		movlw	RTC_Mode			; Mode
		movwf	I2CWordAdr

ModeCode	set	0x00
	ifdef	AutoStandby
ModeCode	set	ModeCode | (1<<bAutoStby)
	endif
	ifdef	WakeUpRun
ModeCode	set	ModeCode | (1<<bWakeRun)
	endif
	ifdef	DeapSleeping
ModeCode	set ModeCode | (1<<bDeapSleep)
	endif
	ifdef	EnableHourSign
ModeCode	set	ModeCode | (1<<bHourSign)
	endif
	ifdef	SignMode
ModeCode	set	ModeCode | (1<<bHourSignMode)
	endif
	ifdef	TimeReInit
ModeCode	set ModeCode | (1<<bReinitTime)
	endif
	ifdef	DCF77Enabled
ModeCode	set ModeCode | (1 << bDCFEnabled)
	endif

		movlw	ModeCode			; Init Mode
		movwf	Mode
		call	I2CByteWrite

		clrw						; Init Mode2
		movwf	Mode2
		call	I2CByteWrite

		movlw	.24
		movwf	FSR
		movlw	RTC_DCFData
		call	SetWordAdr			; Set RTC's slave address
DCFLoop
		clrw
		call	I2CByteWrite
		decfsz	FSR,f
		goto	DCFLoop

;- Clear alarm data in RTC
AlmClrRtc							; Clears alarm data and message in RTC
		movlw	Alarm0WDay
		movwf	FSR
		movlw	RTC_AlarmData		; Address of alarm data in RTC
		call	SetWordAdr			; Set RTC's slave address

AlmClrRtcLp
		clrw
		call	I2CByteWrite
		incfsz	FSR,f
		goto	AlmClrRtcLp
		return

;******************************************************************************
; Convert binary number to BCD

Conv2BCD							; Convert w to BCD (0 <= w <= 99)
		clrf	BCD					; Clear high nibble
l_bcd_1
		addlw	-.10				; Sub .10
		incf	BCD,f				; Inc high nibble
		btfsc	STATUS,C			; Loop if no carry
		goto	l_bcd_1
		addlw	.10					; Re add .10, C=1
		decf	BCD,f				; Correct high nibble
		swapf	BCD,f				; Swap to high part of byte
		addwf	BCD,w				; Add to low nibble, C=0
		return

;******************************************************************************
; Writes the time to RTC

StoreTime
		call	SetSlaveAdr			; Set Slave address of RTC

		movlw	0x80				; Disable counting command
		call	SendRtcCmd
									; I2CWordAdr = 0x01
		clrw
		btfsc	Second2,0			; Store subsec
		addlw	0x50
		call	I2CBcdWrite
									; I2CWordAdr = 0x02

		clrc						; carry = 0
		rrf		Second2,w			; Store Second = (Seconds2 >> 1)
		call	I2CBcdWrite
									; I2CWordAdr = 0x03
		movf	Minute,w			; Store Minute
		call	I2CBcdWrite
									; I2CWordAdr = 0x04
		movf	Hour,w				; Store Hour
		call	I2CBcdWrite
									; I2CWordAdr = 0x05

		bcf		Rs232Flags,bTimeSet	; Clear time set flag

StoreDate
		movlw	RTC_Day				; Address of Day
		movwf	I2CWordAdr
									; I2CWordAdr = 0x05

		movf	Day,w
		call	Conv2BCD
		btfsc	Year,0
		iorlw	0x40
		btfsc	Year,1
		iorlw	0x80
		call	I2CByteWrite		; Store Day and Year bit 1..0
									; I2CWordAdr = 0x06
		movf	Month,w				; Store Month and Wday
		call	I2CBcdWrite
									; I2CWordAdr = 0x07
		call	EnableCount
		bcf		flags,bTimeInv

StoreYear
		movlw	RTC_Year			; Address of Year
		movwf	I2CWordAdr
		movf	Year,w				; Write Year
		call	I2CByteWrite
									; I2CWordAdr = 0x11
		comf	Year,w
		call	I2CByteWrite		; Write complement of Year

		movlw	RTC_Century			; Address of Century
		movwf	I2CWordAdr
		movf	Century,w			; Write Year
		goto	I2CByteWrite

; Send enable count command to RTC
EnableCount
		movlw	0x00				; Enable counting command

; Send command to RTC
SendRtcCmd
		clrf	I2CWordAdr
		goto	I2CByteWrite

;******************************************************************************
;	I2C low level functions
;******************************************************************************

; Temporary variables are in common Ram - Can be used from Bank of TRISA too
; START selects BANK1 to access TRISA	- STOP select BANK0 before return

; At reset SCL and SDA bits in PORTA are cleared

;******************************************************************************
; Reads a byte from RTC to Scratch, address is in w
; store address in I2CWordAdr, I2CWordAdr incremented afther execution

I2CByteReadStoreAddr
	movwf	I2CWordAdr			; Store address

; Reads a byte from RTC to Scratch, address is in I2CWordAdr
; I2CWordAdr incremented afther execution

I2CByteRead
	CALL	START				; Generate Start
	MOVF	I2CSlaveAdr,w		; Get slave address for write
	CALL	OUT_BYTE			; Send slave address byte + nack
	CALL	OUT_BYTE_ADDR		; Send word	 address byte + nack

	CALL	START				; Generate repeted start
	INCF	I2CSlaveAdr,w		; Get slave address for read
	CALL	OUT_BYTE			; Send byte + nack
	CALL	IN_BYTE				; Get the byte to Scratch + send nack

ToStop
	CALL	STOP				; Generate stop condition
	incf	I2CWordAdr,f		; Increment word address
	movf	Scratch,W			; Return data just read
	bcf		STATUS,RP0			; Back to Bank 0
	return

;******************************************************************************
; Writes W to RTC in BCD format, address is in I2CWordAdr
; I2CWordAdr incremented afther execution

I2CBcdWrite
	call	Conv2BCD			; Convert w to BCD

; Writes W to RTC, address is in I2CWordAdr
; I2CWordAdr incremented afther execution

I2CByteWrite
	movwf	Scratch				; Save data to be written to RTC
	CALL	START				; Generate Start
	MOVF	I2CSlaveAdr,w		; Get slave address for write
	CALL	OUT_BYTE			; Send slave address byte + nack
	CALL	OUT_BYTE_ADDR		; Send word	 address byte + nack
	MOVF	Scratch, 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		PORTA,bSDA			; 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 in a byte from I2C bus, clock out nack, store data to Scratch

IN_BYTE							; Read byte on i2c bus
	CALL	HIGH_SDA			; Configure SDA as input
	movlw	.8
	movwf	I2CBitCnt
;	bsf		I2CBitCnt,3			; Load 8 to I2CBitCnt, OUT_BYTE cleared I2CBitCnt
IN_BIT
	CALL	HIGH_SCL			; SCL -> 1 ; 5us wait
	BCF		STATUS,RP0			; Bank 0 to read PORTA
	BCF		STATUS,C			; clear carry
	BTFSC	PORTA,bSDA			; test SDA bit
	BSF		STATUS,C			; set carry if SDA == 1
	BSF		STATUS,RP0			; Bank 1 to control TRISA
	RLF		Scratch,F			; Scratch = (Scratch << 1) | input bit
	CALL	LOW_SCL				; SCL -> 0 ; 5us wait
	DECFSZ	I2CBitCnt,F			; decrement bit counter
	GOTO	IN_BIT
	GOTO	NACK

;******************************************************************************
; Shift out a byte to I2C bus, clock in (n)ack, get data from I2CWordAdr

OUT_BYTE_ADDR
	MOVF	I2CWordAdr,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	I2CShiftReg			; Store data to send
	MOVLW	.8
	MOVWF	I2CBitCnt			; Loop for 8 bits
OUT_BIT:
	BTFSC	I2CShiftReg,7		; if  one, send a  one
	CALL	HIGH_SDA			; SDA at logic one
	BTFSS	I2CShiftReg,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		I2CShiftReg,F		; left shift, move mext bit to bit7
	DECFSZ	I2CBitCnt,F			; decrement bit counter - Leaves with I2CBitCnt = 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		TRISA,bSDA			; 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
	BSF		TRISA,bSCL			; make SCL pin an input

								; provides nominal >5 us delay @20MHz
								; 1 instuction takes 200ns
	MOVLW	 .9					; 0.2 us
	MOVWF	BCD					; 0.2 us
DELAY_2:						; Loop of 3 inst. time
	DECFSZ	BCD, F				; 8*0.2+0.4 us
	GOTO	DELAY_2				; 8*0.4 us

; Make SCL low by making it output

LOW_SCL:						; SCL -> 0 ; 5 us wait
	BCF		TRISA,bSCL			; make SCL pin an output
	GOTO	DELAY_5US

;******************************************************************************
; Make SCL high by making it input

HIGH_SCL:						; SCL -> 1 ; wait 5 us
	BSF		TRISA,bSCL			; 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	BCD					; 0.2 us
DELAY_1:						; Loop of 3 inst. time
	DECFSZ	BCD, F				; 7*0.2+0.4 us
	GOTO	DELAY_1				; 7*0.4 us
	RETURN						; Return 0.4 us

;******************************************************************************
; Command routines
;******************************************************************************

;******************************************************************************

UART_SEND_COMMAND
		movf	Rs232Cmd,w
		addlw	'@'					; Answare code in uppercase
		goto	UART_SEND

;******************************************************************************
; Send version

SendVer
		call	UART_SEND_COMMAND
		movlw	VersionMajor		; Send major version
		call	UART_SEND_BYTE
		movlw	'.'
		call	UART_SEND
		movlw	VersionMinor		; Send minor version
UART_SEND_BYTE_CR
		call	UART_SEND_BYTE		; Send a byte and a Cr Lf
SendCrLf
		movlw	high(CrLf)			; Send a Cr Lf sequence
		movwf	Rs232StrPtrH
		movlw	low(CrLf)
		goto	SEND_STRING

;******************************************************************************
; Clear seconds

ClrSec
		clrf	Second2				; Clear seconds
		goto	NewTim

;******************************************************************************
; Set Time

SetTime
		call	UART_REC_BCD		; Get hour
		movwf	Hour
		call	UART_REC_BCD		; Get minute
		movwf	Minute
		call	UART_REC_BCD		; Get second
		movwf	Second2
		addwf	Second2,f			; Doubble it
NewTim
		call	InitSubSecCnt
		bsf		Rs232Flags,bTimeSet	; Sign time has to be saved
		bsf		flags,bNewTime		; Set new time ready

;******************************************************************************
; Get Time

GetTime
		btfsc	flags,bNewTime
		call	TimeCheck
		call	UART_SEND_COMMAND
		movf	Hour,w				; Send hour
		call	UART_SEND_BCD
		movf	Minute,w			; Send minute
		call	UART_SEND_BCD
		clrc						; Get second
		rrf		Second2,w
		goto	UART_SEND_BCD_CR	; Send second

;******************************************************************************
; Set Date and day of week

SetDate
		call	UART_REC_BCD		; Get century
		movwf	Century
		call	UART_REC_BCD		; Get year
		movwf	Year
		call	UART_REC_BCD		; Get month
		addlw	0
		btfsc	STATUS,Z			; Correct 0 to 1
		addlw	1
		movwf	Month
		call	UART_REC_BCD		; Get day
		addlw	0
		btfsc	STATUS,Z			; Correct 0 to 1
		addlw	1
		movwf	Day
		call	UART_REC_BCD		; Get day of week
		bsf		Rs232Flags,bTimeSet	; Sign time has to be saved
		bsf		flags,bNewTime		; Set new time ready

;******************************************************************************
; Get Date and day of week

GetDate
		btfsc	flags,bNewTime
		call	TimeCheck
		call	UART_SEND_COMMAND
		movf	Century,w
		call	UART_SEND_BCD		; Send century
		movf	Year,w
		call	UART_SEND_BCD		; Send year
		movf	Month,w
		call	UART_SEND_BCD		; Send month
		movf	Day,w
		call	UART_SEND_BCD		; Send day
		incf	WDay,w				; Convert 0..6 to 1..7
UART_SEND_BCD_CR
		call	UART_SEND_BCD		; Send day of week
		goto	SendCrLf

;******************************************************************************
; Init Alarm pointers (IRP:FSR and RTC)

AlarmPtr
		call	UART_REC_BCD		; Get alarm number
		movwf	FSR
		movf	Alarms,w			; There are 40 alarm entries on 16F87, 16F88
		subwf	FSR,w				; 20 on others
		decf	Alarms,w			; If number > limit use last alarm entry
		btfsc	STATUS,C
		movwf	FSR
		movf	FSR,w
		movwf	Scratch3			; Store it
		addwf	FSR,f				; One entry has 4 locations
		rlf		FSR,f
		movf	FSR,w
		movwf	I2CWordAdr

		movlw	.4*.20				; Alarm entries >=20 are on Bank3
		subwf	FSR,w
		btfss	STATUS,C
		goto	PtrOk
		movwf	FSR					; Modify pointer
		bsf		STATUS,IRP			; Use Bank3

PtrOk
		movlw	Alarm0WDay
		addwf	FSR,f				; Init memory pointer

		movlw	RTC_AlarmData
		addwf	I2CWordAdr,w		; Init RTC address

SetWordAdr
		movwf	I2CWordAdr			; Save word address
SetSlaveAdr
		movlw	RTC_ADDR			; Init Slave address
		movwf	I2CSlaveAdr
		return

;******************************************************************************
; Set Alarm

SetAlarm
		call	AlarmPtr			; Get pointer to alarm table
		call	UART_REC_BYTE		; Get enable flags
		movwf	INDF				; Store it in memory
		call	I2CByteWrite		; Store it in RTC
		incf	FSR,f				; Move pointer
		call	UART_REC_BCD		; Get alarm hour
		movwf	INDF				; Store it in memory
		call	I2CByteWrite		; Store it in RTC
		incf	FSR,f
		call	UART_REC_BCD		; Get alarm minute
		movwf	INDF				; Store it in memory
		call	I2CByteWrite		; Store it in RTC
		incf	FSR,f
		call	UART_REC_BYTE		; Get alarm mode, duration
		movwf	INDF				; Store it in memory
		call	I2CByteWrite		; Store it in RTC

		bcf		FSR,0
		bcf		FSR,1				; Move pointer to beginning of entry
		goto	SendAlarm

;******************************************************************************
; Get Alarm

GetAlarm
		call	AlarmPtr			; Get pointer to alarm table
SendAlarm
		call	UART_SEND_COMMAND
		movf	Scratch3,w
		call	UART_SEND_BCD		; Send alarm number
		movf	INDF,w				; Get alarm enable flags
		call	UART_SEND_BYTE
		incf	FSR,f
		movf	INDF,w				; Get alarm hour
		call	UART_SEND_BCD
		incf	FSR,f
		movf	INDF,w				; Get alarm minute
		call	UART_SEND_BCD
		incf	FSR,f
		movf	INDF,w				; Get alarm mode, duration
CLR_IRP_SEND_B_CR
		bcf		STATUS,IRP			; IRP = 0
		goto	UART_SEND_BYTE_CR

;******************************************************************************
; Set PWM data

SetPWM
		movlw	RTC_PWMData			; Init RTC word address to PWM data
		call	SetWordAdr
		call	UART_REC_BYTE		; Get pulse width for run mode
		movwf	CCP1_Run
		call	I2CByteWrite		; Write to RTC
		call	UART_REC_BYTE		; Get period for run mode

		call	I2CByteWrite
		call	UART_REC_BYTE		; Get pulse width for sleep mode
		movwf	CCP1_Sleep
		call	I2CByteWrite
		call	UART_REC_BYTE		; Get period for sleep mode

		call	I2CByteWrite

		movf	CCP1_Run,w			; Copy new settings to CCPR1L and PR2
		btfss	flags,bCoilPower	; Coil powered up?
		movf	CCP1_Sleep,w		; No - Use sleep variable
		movwf	CCPR1L

;******************************************************************************
; Get PWM data

GetPWM
		call	UART_SEND_COMMAND
		movf	CCP1_Run,w
		call	UART_SEND_BYTE		; Send pulse width for run mode
		movlw	.49
		call	UART_SEND_BYTE		; Send period for run mode
		movf	CCP1_Sleep,w
		call	UART_SEND_BYTE		; Send pulse width for sleep mode
		movlw	.49
		call	UART_SEND_BYTE		; Send period for sleep mode
		movf	CCPR1L,w
		call	UART_SEND_BYTE		; Send current pulse width
		bsf		STATUS,RP0
		movf	PR2,w
		bcf		STATUS,RP0
		goto	UART_SEND_BYTE_CR	; Send current periode

;******************************************************************************
; Set Auto Sleep Timer

SetAST
		movlw	RTC_AST
		call	SetWordAdr
		call	UART_REC_BYTE		; Get high byte of AST
		movwf	AutoStbyTimH
		call	I2CByteWrite
		call	UART_REC_BYTE		; Get low  byte of AST
		movwf	AutoStbyTimL
		call	I2CByteWrite

;******************************************************************************
; Get Auto Sleep Timer

GetAST
		call	UART_SEND_COMMAND
		movf	AutoStbyTimH,w		; Send high byte of AST
		call	UART_SEND_BYTE
		movf	AutoStbyTimL,w
		goto	UART_SEND_BYTE_CR	; Send low	byte of AST

;******************************************************************************
; Set Mode

SetMode
		movlw	RTC_Mode
		call	SetWordAdr
		call	UART_REC_BYTE		; Get mode
		movwf	Mode
		call	I2CByteWrite
		btfsc	flags,bStandBy
		call	InitPWMLowPower
		call	UART_REC_BYTE		; Get mode
		movwf	Mode2
		call	I2CByteWrite

;******************************************************************************
; Get Mode

GetMode
		call	UART_SEND_COMMAND
		movf	Mode,w
		call	UART_SEND_BYTE		; Send mode
		movf	Mode2,w
		goto	UART_SEND_BYTE_CR	; Send mode2

;******************************************************************************
; Set relais state

SetRel
		call	UART_REC_BYTE		; Get relais controll
		andlw	1					; Keep lsb only
		movwf	Scratch
		call	UART_REC_BYTE		; Get duration
		btfss	Scratch,0
		goto	SetRel0

		movwf	SwitchCnt
		bsf		PORTB,bRelais		; Turn on relais output
		goto	SetRel2

SetRel0
		clrf	SwitchCnt			; Clear counter
		bcf		PORTB,bRelais		; Turn off relais output
SetRel2

;******************************************************************************
; Get relais state

GetRel
		call	UART_SEND_COMMAND
		clrw
		btfsc	PORTB,bRelais		; Get relais state
		addlw	1
		call	UART_SEND_BYTE		; Send it
		movf	SwitchCnt,w
		goto	UART_SEND_BYTE_CR	; Send remaining on time

;******************************************************************************
; Get number of alarms

GetAln
		call	UART_SEND_COMMAND
		movf	Alarms,w			; There are .20 alarms for 16F628(A) and 16F648A
		call	UART_SEND_BCD		; There are .40 alarms for 1687 and 16F88
		movlw	RemoteAddr
		call	UART_SEND_BYTE		; Send RC5 address of clock
		movlw	PROCCODE
		call	UART_SEND_BYTE		; Send controller type
		movlw	0x0C
		goto	UART_SEND_BYTE_CR	; Send features

;******************************************************************************
; Set hour sound parameters

SetHSnd
		movlw	RTC_HourSnd
		call	SetWordAdr

		call	UART_REC_BCD		; Get begin hour
		movwf	HourSndBeg
		call	I2CByteWrite

		call	UART_REC_BCD		; Get end	hour
		movwf	HourSndEnd
		call	I2CByteWrite

		call	UART_REC_BYTE		; Get light limit	Not impelented

;******************************************************************************
; Get hour sound parameters

GetHSnd
		call	UART_SEND_COMMAND
		movf	HourSndBeg,w
		call	UART_SEND_BCD		; Send begin hour
		movf	HourSndEnd,w
		call	UART_SEND_BCD		; Send end	 hour
		clrw
		goto	UART_SEND_BYTE_CR	; Send light limit

;******************************************************************************
; Get / Set propeller state

SetProp
		call	UART_REC_BYTE		; Get controll info
		movwf	Scratch
		btfss	Scratch,7			; If bit 7 cleared
		goto	SendProp			;  read flags only

		btfsc	Scratch,0			; Bit 0 set
		call	NotStandbyPb		;  start propeller
		btfss	Scratch,0			; Bit 0 cleared
		call	ToStandby			;  stop	 propeller

SendProp
		call	UART_SEND_COMMAND
		movf	flags,w				; Send flags
		andlw	0xBF
		bsf		STATUS,RP0			; Change Time check needed flag to deep sleep
		btfsc	TRISB,bTrafo
		iorlw	0x40
		bcf		STATUS,RP0
		call	UART_SEND_BYTE
		movf	StandbyTimerH,w		; Send high byte of remaining run time
		call	UART_SEND_BYTE
		movf	StandbyTimerL,w
		goto	UART_SEND_BYTE_CR	; Send low	byte of remaining run time


;******************************************************************************
; Execute Rc5 command

XeqRc5
		call	UART_REC_BYTE		; Get address code
		andlw	0x1F				; Only 5 bit valid
		movwf	RC5_Addr
		call	UART_REC_BYTE		; Get command code
		movwf	RC5_Cmd
		bsf		RC5_flags,bRC5_DataReady; Set address and command valid
		return						; Answare will be send in ProcessRC5

;******************************************************************************
; Init I2C slave and word address

InitI2CAdrs
		call	UART_REC_BYTE		; Get slave address
		andlw	0xFE				; Mask R/W bit off
		movwf	I2CSlaveAdr
		call	UART_REC_BYTE		; Get word address
		movwf	I2CWordAdr
		return

;******************************************************************************
; Set reg on I2C bus

SetI2C
		call	InitI2CAdrs
		call	UART_REC_BYTE		; Get data to be written
		call	I2CByteWrite		; Write it, increments I2CWordAdr
		decf	I2CWordAdr,f		; Correct it
		goto	SendI2CReg

;******************************************************************************
; Get reg on I2C bus

GetI2C
		call	InitI2CAdrs
SendI2CReg
		call	UART_SEND_COMMAND
		movf	I2CSlaveAdr,w
		call	UART_SEND_BYTE		; Send slave address
		movf	I2CWordAdr,w
		call	UART_SEND_BYTE		; Send word address
		call	I2CByteRead			; Read the data
		goto	UART_SEND_BYTE_CR	; Send it

;******************************************************************************
; Init FSR

InitFsr
		call	UART_REC_BYTE		; Get high part of address
		andlw	1
		movwf	Scratch
		bcf		STATUS,IRP
		btfsc	Scratch,0			; If on Bank2-3
		bsf		STATUS,IRP			; IRP=1
		call	UART_REC_BYTE		; Get low  part of address
		movwf	FSR
		return

;******************************************************************************
; Set internal reg

SetReg
		call	InitFsr				; Init FSR
		call	UART_REC_BYTE		; Get data
		movwf	INDF				; Write to reg
		goto	SendReg

;******************************************************************************
; Get internal reg

GetReg
		call	InitFsr				; Init FSR
SendReg
		call	UART_SEND_COMMAND
		movf	Scratch,w
		call	UART_SEND_BYTE		; Send high part of address
		movf	FSR,w
		call	UART_SEND_BYTE		; Send low	part of address
		movf	INDF,w				; Read reg
		goto	CLR_IRP_SEND_B_CR	; Send it

;******************************************************************************
; Set relais on time

SetRelIr
		call	UART_REC_BYTE	; Get control byte
		andlw	0x80
		movwf	Scratch3
		btfss	Scratch3,7
		goto	GetRelIr

		movlw	RTC_RelOnIr
		call	SetWordAdr
		call	UART_REC_BYTE	; Get relais on time
		movwf	RelOnIr
		call	I2CByteWrite	; Write it to RTC

GetRelIr
		call	UART_SEND_COMMAND
		movf	Scratch3,w
		call	UART_SEND_BYTE	; Send control byte
		movf	RelOnIr,w
		goto	UART_SEND_BYTE_CR; Send relais on time

;******************************************************************************
; Clear alarm sound

ClrAlmSnd
		call	UART_REC_BYTE	; Get control byte
		andlw	0x80
		movwf	Scratch3
		btfss	Scratch3,7
		clrf	SoundCnt

GetALmSnd
		call	UART_SEND_COMMAND
		movf	Scratch3,w
		call	UART_SEND_BYTE	; Send control byte
		movf	SoundCnt,w
		goto	UART_SEND_BYTE_CR; Send Sound counter

SetOffs

		call	UART_REC_BYTE		; Get control byte
		andlw	0xE0
		movwf	Scratch3
		call	UART_REC_BYTE		; Get temperature offset

		call	UART_REC_BYTE		; Get humidity offset

		call	UART_REC_BYTE		; Get pressure offset

;GetOffs
		call	UART_SEND_COMMAND
		movf	Scratch3,w
		call	UART_SEND_BYTE
		clrw
		call	UART_SEND_BYTE		; Send temperature offset
		clrw
		call	UART_SEND_BYTE		; Send humidity offset
		clrw
		goto	UART_SEND_BYTE_CR	; Send pressure offset

;******************************************************************************
; Get measurements

GetMeas
		call	UART_SEND_COMMAND
		clrw
		call	UART_SEND_BYTE		; Send Light
		clrw
		call	UART_SEND_BYTE		; Send Temperature
		clrw
		call	UART_SEND_BYTE		; Send Humidity
		clrw
		call	UART_SEND_BYTE		; Send Pressure high byte
		clrw
		goto	UART_SEND_BYTE_CR	; Send Pressure low byte

;******************************************************************************
; Get DCF77 state

GetDCF
		call	UART_SEND_COMMAND
		movf	DCF_SNum,w			; Get number of successfull synchronizations
		call	UART_SEND_BYTE		; Send DCF77 state
		movlw	DCF_Year+1
		movwf	FSR
		call	UART_SEND_BYTE_DI	; Send DCF77 Year
		call	UART_SEND_BYTE_DI	; Send DCF77 Month

		decf	FSR,f
		movf	INDF,w				; Get DCF77 Day
		btfsc	DCF_flags,bDCFReady	; Copy status bits
		iorlw	1
		btfsc	DCF_flags,bDCFWaitSync
		iorlw	2
		call	UART_SEND_BYTE		; Send Day
		decf	FSR,f
		movf	INDF,w				; Get DCF77 Hour
		btfsc	DCF_flags,bDCFError	; Copy status bits
		iorlw	1
		btfsc	DCF_flags,bDCFParityErr
		iorlw	2
		call	UART_SEND_BYTE		; Send Hour
		decf	FSR,f
		movf	INDF,w				; Get DCF77 Minute
		btfsc	DCF_flags,bDCFSummerTime; Copy status bits
		iorlw	1
		call	UART_SEND_BYTE		; Send DCF77 Minute
		movf	DCF_PNum,w
		goto	UART_SEND_BYTE_CR
									; DCF77 Seconds not transmitted

UART_SEND_BYTE_DI
		decf	FSR,f
		movf	INDF,w
		goto	UART_SEND_BYTE

;******************************************************************************
; Set EEProm

SetEE
		call	UART_REC_BYTE		; Get address byte
		movwf	Scratch
		call	UART_REC_BYTE		; Get data byte
		movwf	Scratch2

		movf	Scratch,w
		banksel	EEADR
		movwf	EEADR
		movf	Scratch2,w
		movwf	EEDATA
		banksel	EECON1
		bsf		EECON1,WREN
		clrf	Scratch3
		btfsc	INTCON,GIE
		bsf		Scratch3,GIE
		bcf		INTCON,GIE
		movlw	0x55
		movwf	EECON2
		movlw	0xAA
		movwf	EECON2
		bsf		EECON1,WR

		btfsc	Scratch3,GIE
		bsf		INTCON,GIE

lEEW
		btfsc	EECON1,WR
		goto	lEEW

		bcf		EECON1,WREN
		clrf	STATUS
		goto	SendEE

;******************************************************************************
; Get EEProm

GetEE
		call	UART_REC_BYTE		; Get address byte
		movwf	Scratch

SendEE
		call	UART_SEND_COMMAND
		movf	Scratch,w
		call	UART_SEND_BYTE		; Send address
		movf	Scratch,w
		banksel	EEADR
		movwf	EEADR
		banksel	EECON1
		bsf		EECON1,RD
		banksel	EEDATA
		movf	EEDATA,w
		clrf	STATUS
		goto	UART_SEND_BYTE_CR	; Send data just read from EEprom

;******************************************************************************

CalcWDay							; Calculates day of week
		clrf	Scratch
		clrf	Scratch2
		clrf	Scratch3
		movf	Day,w				; Day changes at 00:00
		movwf	Scratch4

;Year16bit							; Convert Centuty and Year to a 16 bit word in Rs232Temp:Rs232Status
		movf	Year,w
		movwf	Rs232Status
		clrf	Rs232Temp
		movf	Century,w
		movwf	BCD
		btfsc	STATUS,Z
		goto	ClearCy
l_year16
		movlw	.100
		addwf	Rs232Status,f
		btfsc	STATUS,C
		incf	Rs232Temp,f
		decfsz	BCD,f
		goto	l_year16
ClearCy
		clrc						; Rs232Temp:Rs232Status = .100 * Century + Year

JDN									; Returns Julian Day Number
									; Scratch .. Scratch3 cleard before call
									; For WDay and SDate calculation Scratch4 = Day
									; For JDN if Hour < .12 then (Day - 1) else Day

		movf	Month,w
		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
		addwf	Scratch4,w			; Add the day
		movwf	Scratch3			; Scratch2:Scratch3 = day + (.153 * m + 2) div 5
		btfsc	STATUS,C
		incf	Scratch2,f

		bcf		STATUS,C			; C = 0
		btfsc	STATUS,IRP
		decfsz	Rs232Status,f
		goto	SetY
		decf	Rs232Temp,f			; Rs232Temp:Rs232Status = .100 * Century + Year - a
SetY
		rrf		Rs232Temp,w
		movwf	BCD
		rrf		Rs232Status,w
		movwf	Scratch4			; BCD:Scratch4 = (.100 * Century + Year - a) div 2

Mul365
		movlw	low(.365)
		addwf	Scratch3,f
		movlw	high(.365)
		call	ChkCy
		addwf	Scratch2,f
		btfsc	STATUS,C
		incf	Scratch,f
		decfsz	Rs232Status,f
		goto	Mul365
		decf	Rs232Temp,f
		btfss	Rs232Temp,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
		addwf	Scratch2,f
		movlw	0x1A
		call	ChkCy
		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

		movf	Century,w			; W = (.100 * Century + Year) div .100 = Century
		btfss	STATUS,IRP
		goto	Ymod100
		movf	Year,f
		btfsc	STATUS,Z			; Correct W if Year = 0
		addlw	-1
Ymod100
		movwf	Rs232Status			; Rs232Status = 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		Rs232Status,f
		rrf		Rs232Status,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
		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
		clrf	STATUS				; Clear IRP

FXD3216UBy700
		clrf	Rs232Fsr			; 256*JDN / 0x700
		clrf	Scratch4
		movlw	7
FXD3216U_StoreDivH
		movwf	I2CSlaveAdr			; At return W and Rs232Temp holds WDay (0 - Monday, 6 - Sunday)

;Inputs:
;	Dividend - Scratch:Scratch2:Scratch3:Scratch4 - Scratch is the most significant
;	Divisor	 - I2CSlaveAddr:Rs232Fsr
;Temporary:
;	Counter	 - BCD
;	Remainder- Rs232Temp:Rs232Status
;Output:
;	Quotient - Scratch:Scratch2:Scratch3:Scratch4
;

FXD3216U
		CLRF	Rs232Status			; Clear result
		CLRF	Rs232Temp
		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		Rs232Status,F		;shift carry into remainder
		RLF		Rs232Temp,F

		RLF		BCD,F				;save carry in counter

		MOVF	Rs232Fsr,W			;substract divisor from remainder
		SUBWF	Rs232Status,F
		MOVF	I2CSlaveAdr,W
		BTFSS	STATUS,C
		INCF	I2CSlaveAdr,W
		SUBWF	Rs232Temp,W			;keep that byte in W untill we make sure about borrow

		SKPNC						;if no borrow
		BSF		BCD,0				;set bit 0 of counter (saved carry)

		BTFSC	BCD,0				;if no borrow
		GOTO	UOK46LL				;jump

		MOVF	Rs232Fsr,W			;restore remainder if borrow
		ADDWF	Rs232Status,F
		MOVF	Rs232Temp,W			;read high byte of remainder to W
									;to not change it by next instruction
UOK46LL
		MOVWF	Rs232Temp			;store high byte of remainder
		CLRC						;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
		RETURN

;******************************************************************************
; Get number of days for JDN

GetDn
		movlw	high(JDNTabl)
		movwf	PCLATH
		movf	BCD,w
		addlw	low(JDNTabl)
		movwf	PCL

ChkCy
		btfsc	STATUS,C
		addlw	1
		return

;******************************************************************************

	org	0x0797

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

;******************************************************************************

	org	0x07A5

;******************************************************************************
; String tables

InitMsg								; String tables (last character of string has bit7 set)
	dt	"Propeller clock"
CrLf
	retlw	0x0a
	retlw	0x8d					; Sign end of string - bit7 set


	org	0x7B6

;******************************************************************************
; Jump tables

;******************************************************************************
; Return parameter count of a command

CmdParNum
	addwf	PCL,f
	retlw	.4		;'`'	0x60
	retlw	.4		;'a'	0x61
	retlw	.10		;'b'	0x62
	retlw	0		;'c'	0x63
	retlw	0		;'d'	0x64
	retlw	.6		;'e'	0x65
	retlw	.4		;'f'	0x66
	retlw	.4		;'g'	0x67
	retlw	0		;'h'	0x68
	retlw	.4		;'i'	0x69
	retlw	0		;'j'	0x6A
	retlw	.10		;'k'	0x6B
	retlw	.2		;'l'	0x6C
	retlw	.8		;'m'	0x6D
	retlw	0		;'n'	0x6E
	retlw	0		;'o'	0x6F
	retlw	0		;'p'	0x70
	retlw	.6		;'q'	0x71
	retlw	0		;'r'	0x72
	retlw	.6		;'s'	0x73
	retlw	0		;'t'	0x74
	retlw	.4		;'u'	0x75
	retlw	0		;'v'	0x76
	retlw	.2		;'w'	0x77
	retlw	.4		;'x'	0x78
	retlw	0		;'y'	0x79
	retlw	.4		;'z'	0x7A
	retlw	0		;'{'	0x7B
	retlw	0		;'|'	0x7C
	retlw	0		;'}'	0x7D
	retlw	.2		;'~'	0x7E
	retlw	.8		;''	0x7F

;******************************************************************************
; Go to execution routine of a command

CmdExec
	addwf	PCL,f			;	; Commands and parameters		; Answare
							;	; -- Time and date parameters transfered in BCD --
							;	; -- #YY, #MM, #DD, #hh, #mm, #ss, #nn, #hb, #he
			goto	SetRelIr;`	; Get / Set relais on time for ir commands
								;				'`' #Rc #Ro		; '@' #Ro
								;		Rc= Controll 0x80 for read time only
								;		Ro= Relais on time in minutes

								; Example: Send:'`8000'					Receive: '@80FF'


			goto	XeqRc5	;a	; Execute Rc5 command #aa #cc	; 'A' #aa #cc
								; On RC5 receive					Receive: 'Aaacc'
								;		aa= RC5 address code (5 bits)
								;		cc= RC5 command code - bit7 toggle bit

								; Example: Send:'a1D0C'				Receive: 'A1d0c'

			goto	SetDate	;b	; Set date 'b' #CC #YY #MM #DD #wd	; 'B' #CC #YY #MM #DD #wd
								;		YY= Year		00..99
								;		MM= Month		01..12
								;		DD=	Day			01..(28,29,30,31)
								;		wd= Day of week 01..07

								; Example: Send:'b2009042007'		Receive: 'B2009042007'

			goto	ClrSec	;c	; Clear seconds	'c'				; 'C' #hh #mm #ss
								;		hh=00..23, mm=00..59, ss=00

								; Example: Send:'c'					Receive: 'C073000'

			goto	GetDate	;d	; Get date 'd'					; 'D' #CC #YY #MM #DD #wd

								; Example: Send:'d'					Receive: 'B2009042007'

;;V;; For debugging only
			goto	SetI2C	;e	; Write reg on I2C 'e' #Sa #Wa #va; 'E' #Sa #Wa #va
								;		Sa= Slave address, Wa= Word address, Va=value

								; Example: Send:'eA00F20'			Receive: 'Ea00f20'

			goto	GetI2C	;f	; Read reg on I2C 'f' #Sa #Wa	; 'F' #Sa #Wa #va

								; Example: Send:'fA00F'				Receive: 'fa00f20'
;;^;; For debugging only

			goto	SetMode	;g	; Set mode 'g' #Mo #Mo2			; 'G' #Mo #Mo2
								;		Mo= Mode
								;	mode:	bit	0			; Auto standby timer enable
								;			bit	1			; Wake Run afther power up
								;			bit	2			; No coil drive in sleep mode
								;			bit	3			; Sound at hour change
								;			bit	4			; Hour sign mode (One sound / as many as hour (1..12))
								;			bit	5
								;			bit	6			; Enable DCF77 synchronization
								;			bit	7			; Enable reinit time from RTC at midnight
								;	mode2:	bit	0			; Motor PWM enabled
								;			bit	1			; Manual speed control
								;			bit	2			; Not implemented
								;			bit	3			; Low speed
								;			bit 7..4		; Not implemented

								; Example: Send:'g0100'				Receive: 'G0100'

			goto	GetMode	;h	; Get mode 'h'					; 'H' #Mo #Mo2
								;		Mo= Mode
								;			bit	0			; Auto standby timer enable
								;			bit	1			; Wake Run afther power up
								;			bit	2			; No coil drive in sleep mode
								;			bit	3			; Sound at hour change
								;			bit	4			; Hour sign mode (One sound / as many as hour (1..12))
								;			bit	5
								;			bit	6			; Enable DCF77 synchronization
								;			bit	7			; Enable reinit time from RTC at midnight
								;	mode2:	bit	0			; Motor PWM enabled
								;			bit	1			; Manual speed control
								;			bit	2			; Not implemented
								;			bit	3			; Low speed
								;			bit 7..4		; Not implemented

								; Example: Send:'h'					Receive: 'H0100'

			goto	SetAST	;i	; Set AST 'i' #Ah #Al			; 'I' #Ah #Al
								;			Ah:Al= 16 bit AutoStandby Time in seconds

								; Example: Send:'i0E10'				Receive: 'I0e10'

			goto	GetAST	;j	; Get AST 'j'					; 'J' #Ah #Al
								;			Ah:Al= 16 bit AutoStandby Time in seconds

								; Example: Send:'j'					Receive: 'J0e10'

			goto	SetAlarm;k	; Set alarm 'k'#nn #ff #hh #mm #uu; 'K' #nn #ff #hh #mm #uu
								;			nn=00..19, ff: bit7-enab; bit6..0-enable on weekday 7..1
								;			hh=00..23, if hh==32 hour masked off from comparation
								;			mm=00..59,
								;			uu: 7: mode 0= Alarm , 6: Turn propeller on , 5: Turn propeller off , 4: Enable sound
								;			uu: 7: mode 1= Relais, 6..0= switch for (6..0) minutes; 0 minutes turns off relais

								; Example: Send:'k01FF075000'		Receive: 'K01ff075000'
								; Example: Send:'k01FF0750C0'		Receive: 'K01ff0750c0'

			goto	GetAlarm;l	; Get alarm 'l' #nn				; 'L' #nn #ff #hh #mm #uu
								;			nn=00..19, ff: bit7-enab; bit6..0-enable on weekday 7..1
								;			hh=00..23, mm=00..59,
								;			uu: 7: mode 0= Alarm , 6: Turn propeller on , 5: Turn propeller off , 4: Enable sound
								;			uu: 7: mode 1= Relais, 6..0= switch for (6..0) minutes; 0 minutes turns off relais

								; Example: Send:'l01'				Receive: 'L01ff075000'
								; Example: Send:'l01'				Receive: 'L01ff0750c0'

			goto	SetPWM	;m	; Set PWM 'm' #Cr #Pr #Cs #Ps	; 'M' #Cr #Pr #Cs #Ps #Cc #Pc
								;			Cr= CCPR1L setting for Run	 mode
								;			Pr= PR2	   setting for Run	 mode
								;			Cs= CCPR1L setting for Sleep mode
								;			Ps= PR2	   setting for Sleep mode
								;			Cc= current CCPR1L setting
								;			Pc= current PR2    setting

								; Example: Send:'m283D0C3D'			Receive: 'M283d0c3d0c3d'

			goto	GetPWM	;n	; Get PWM 'n'					; 'N' #Cr #Pr #Cs #Ps #Cc #Pc
								;			Cr= CCPR1L setting for Run	 mode
								;			Pr= PR2	   setting for Run	 mode
								;			Cs= CCPR1L setting for Sleep mode
								;			Ps= PR2	   setting for Sleep mode
								;			Cc= current CCPR1L setting
								;			Pc= current PR2    setting

								; Example: Send:'n'					Receive: 'N283d0c3d0c3d'

 			goto	GetAln	;o	; Get number of alarms 'o' 		; 'O' #An #Ad #Pt #Fe
								;			An= Number of available alarms
								;			Ad= RC5 Address of clock
								;			Pt= Processor type			28 for 16F628, 48 for 16F648, 87 for 16F87, 88 for 16F88
								;			Fe= Features
								;				bit 7: Pressure measurement available
								;				bit 6: Humidity measurement available
								;				bit 5: Temperatue measurement available
								;				bit 4: Light measurement available
								;				bit 3: Motor PWM available
								;				bit 2: DCF77 synchron available
								;				bit 1:
								;				bit 0: USB communication available
								;

								; Example: Send:'o'					Receive: 'O201d9007' - on 16F690

		goto SEND_MESSAGE	;p	; Connect 'p'					; "Propeller clock"
								; Example: Send:'p'					Receive: 'Propeller clock'

			goto	SetHSnd	;q	; Set hour sound 'q' #hb #he #ll; 'Q' #hb #he #ll
								;			hb= hour sound begin hour (bcd)
								;			he= hour sound end	 hour (bcd)
								;			ll= light limit - Not implemented

								; Example: Send:'q082080'			Receive: 'Q082000'

			goto	GetHSnd	;r	; Get hour sound 'r'			; 'R' #hb #he
								;			hb= hour sound begin hour (bcd)
								;			he= hour sound end	 hour (bcd)

								; Example: Send:'r'					Receive: 'R0820'

			goto	SetTime	;s	; Set time 's' #hh #mm #ss		; 'S' #hh #mm #ss
								;		hh=00..23, mm=00..59, ss=00..59

								; Example: Send:'s073005'			Receive: 'S073005'

			goto	GetTime	;t	; Get time 't'					; 'T' #hh #mm #ss
								;		hh=00..23, mm=00..59, ss=00..59

								; Example: Send:'t'					Receive: 'T073005'

;;V;; For debugging only
;			goto	SetReg	;u	; Set reg 'u' #rh #rl #vv		; 'U' #vv
;								;		rh:rl=reg address, vv=value
;
;								; Example: Send:'u0110AA'			Receive: 'U0110aa'

			goto	SetEE	;u	; Set EEprom byte 'u' #ad #da		; 'U' #ad #da
								;		ad=reg address, vv=value

								; Example: Send:'u0070'					Receive: 'U0070'

;;^;; For debugging only

			goto	SendVer	;v	; Get version 'v'				; 'V' #ma '.' #mi

								; Example: Send:'v'					Receive: 'V2.0d'

			goto	SetProp	;w	; Get/Set propeller state 'w' #wc; 'W' #ff #Rh #Rl
								;		wc=0x80 get state only
								;		wc=1 for Start
								;		wc=0 for Stop
								;		ff=flags
								;			bit	0:	in standby mode
								;			bit	1:	in sleep mode
								;			bit	2:	high power on coil
								;			bit	3:	index LED on
								;			bit	4:
								;			bit	5:
								;			bit	6:	in deep sleep mode
								;			bit	7:	time invalid
								;		f2=flags2
								;			bit 0:  Need to save time
								;			bit 1:  Night period
								;			bit 2:  Not impelmented
								;			bit 3:  Motor runs at low speed
								;			bit 4:
								;			bit 5:
								;			bit 6:
								;			bit 7:  I2C bus error
								;
								;		Rh:Rl: remaining run time in seconds

								; Example: Send:'w01				Receive: 'W040e10'
								; Example: Send:'w00				Receive: 'W050000'

								;  only read propeller state:
								; Example: Send:'w80'				Receive: 'W430000'

			goto	SetRel	;x	; Set relais 'x' #rr #vv		; 'X' #rr #vv
								;		rr=1 on, r=0 off, vv: on time in minutes

								; Example: Send:'x0103				Receive: 'X0103'

			goto	GetRel	;y	; Get relais 'y'				; 'Y' #rr #Rc
								;		rr=1 on, r=0 off, vv: on time in minutes

								; Example: Send:'y0103'				Receive: 'Y0103'

;;V;; For debugging only
			goto	GetReg	;z	; Send reg 'z' #rh #rl			; 'Z' #vv
								;		rh:rl=reg address, vv=value

								; Example: Send:'z0110'				Receive: 'Z0103aa'
;;^;; For debugging only

			goto	GetDCF	;{	; Get DCF variables '{'				; '[' #Ns #DY #DM #DD #Dh #Dm #Ps
								;		Ns= Number of succesfull synchronizations
								;		DY= bit 7..0: DCF year  (BCD)
								;		DM= bit 7..3: DCF month (BCD), bit 2..0 DCF day of week,
								;		DD= bit 7..2: DCF day   (BCD), bit 1: Wait for DCF77 minute synchron, bit 0: DCF time and date ready and valid
								;		Dh= bit 7..2: DCF hour  (BCD), bit 1: Error in decoding             , bit 0: Parity error
								;		Dm= bit 7..1: DCF minute(BCD). bit 0: Last time decoded used Summer time (CEST)
								;		Ps= Number of succesfull synchronizations in previous hour

								; Example: Send:'{'						Receive: '[00000000000000'

			goto	GetMeas	;|	; Send measures '|'					; '_'  #Lt #Tp #Fh #Ph #Pl
								; Not implemented
								;		Lt= Light
								;		Tp= Temperature
								;		Hy= Humidity
								;		Ph= Air pressure bit 9..8
								;		Pl= Air pressure bit 7..0

								; Example: Send:'|'						Receive: '_0000000000'

			goto	GetEE	;}	; Get EEprom byte '}' #ad #da		; ']' #ad #da
								;		ad=reg address, vv=value

								; Example: Send:'}00'					Receive: ']0070'

			goto	ClrAlmSnd;~	; Clear alarm sound	'~'	#Cb			; '^' #Cb #Sc
								;		Cb= Controll
								;			bit 7: 1 for set alarm sound
								;			bit 3..0: Sound count
								;		SC= Sound counter

								; Example: Send:'~80'					Receive: '^8000'

			goto	SetOffs	;	; Get/Set offsets '' #Ct #To #Ho #Po	; '\' #Ct #To #Ho #Po
								; Not implemented
								;		Cb= Controll
								;			bit 7: for set temperature offset
								;			bit 6: for set humidity    offset
								;			bit 5: for set pressure    offset
								;		To= Temperature offset
								;		Ho= Humidity offset
								;		Po= Pressure offset

								; Example: Send:'E01A2002FD'			Receive: '\E0000000000'

	org		0x7F8

;******************************************************************************
; Get next character of a string

MsgTabl
		movf	Rs232StrPtrH,w		; Get string pointer high byte
		movwf	PCLATH				; Load it to PCLATH
		movf	Rs232StrPtr,w		; Get string pointer low  byte
		incfsz	Rs232StrPtr,f		; Increment pointer as
		goto	lString1
		incf	Rs232StrPtrH,f		; 16 bit number
lString1
		movwf	PCL					; Computed goto to string table
		return						; movwf	PCL cannot be the last instruction

; Deapest Stack						; These processors have an 8 level stack
;	1	call	UARTCmdDec
;	2	call	I2CByteWrite
;	3	CALL	OUT_BYTE
;	4	CALL	CLOCK_PULSE
;	5	CALL	HIGH_SCL
;	6	Interrupt routine
;	7
;	8


;******************************************************************************
;		Define EEProm content
;******************************************************************************


		ORG		0x2100			; Start of message 1 in EEPROM
		de		"Propeller Clock Base ",LastUpdate," ",ProcType," V",VersionMajor+.48,".",((VersionMinor/.16)+.48),((VersionMinor%.16)+.48)

	END								; directive 'end of program'
