;*************************************************************************** 
;* P R O G R A M   F O R   T H E   A V R   F A M I L Y 
;*  
;* File Name		:"sklok.asm" 
;* Title		: Schakelklok
;* Date	 		: 24-04-2000
;* Version 		: 1.00 
;* By			: Hugo Vos 
;* Target MCU		: AT90S8515 
;*
;* E-mail		: hugo@hvos.myweb.nl
;* 
;*************************************************************************** 

.device	AT90S8515	;Prohibits use of non-implemented instructions
.include "8515def.inc"

;***** Code

	rjmp	RESET		; Reset Handle
	rjmp	NOFUNC		; IRQ0
	rjmp	NOFUNC		; IRQ1
	rjmp	NOFUNC		; Timer1 cap
	rjmp	NOFUNC		; Timer1 compA
	rjmp	NOFUNC		; Timer1 compB
	rjmp	TIM1_OVF	; Timer1 overflow
	rjmp	TIM0_OVF	; Timer 0 overflow int.
	rjmp	NOFUNC		; SPI
	rjmp	NOFUNC		; UART RX
	rjmp	NOFUNC		; UART EMPTY
	rjmp	NOFUNC		; UART TX
	rjmp	NOFUNC		; ANA_COMP

;****************************************************************************
;
; Routine if a unexpected interrupt occurs
; 
;****************************************************************************

nofunc:

	nop

	reti
	
;****************************************************************************
;*
;* Main Program MCU pin definition :
;*
;* PortA = PA0 = Display DB0
;*         PA1 = Display DB1
;*	   PA2 = Display DB2
;*         PA3 = Display DB3
;*         PA4 = Display DB4
;*         PA5 = Display DB5
;*         PA6 = Display DB6
;*         PA7 = Display DB7
;* PortB = PB0 = Return key 
;*         PB1 = Select key 
;*         PB2 = + key
;*	   PB3 = - key
;*	   PB4 =
;*         PB5 = 
;*         PB6 =
;*         PB7 = 
;* PortC = PC0 = 
;*	   PC1 = 
;*	   PC2 = 
;*	   PC3 =
;*         PC4 = Display RS 
;*	   PC5 = Display R/W
;*	   PC6 = Display E
;*         PC7 =
;* PortD = control signals
;*	   PD0 = 
;*	   PD1 = 	
;*	   PD2 = 
;*	   PD3 = 	
;*	   PD4 = SCL ( klok line I2C )
;*	   PD5 = SDLO ( data line out I2C )
;*	   PD6 = SDLI ( data line in I2C )
;*	   PD7 =
;*
;***************************************************************************

;***** Main Program Register Variables

.def	SWmod_L		=R14
.def	SWmod_H		=R15
.def	Ax		=R16
.def	Bx		=R17
.def	Cx		=R18
.def	Dx		=R19
.def	dispfunc	=R20
.def	Tempvar		=R21
.def	TcountL		=R22
.def	I2Cstatreg	=R24
.def	TcountH		=R25
.def	disp_refresh	=R23
.def	X_low		=R26
.def	X_high		=R27
.def	Y_low		=R28
.def	Y_high		=R29
.def	Z_low		=R30
.def	Z_high		=R31


;***** Main Programm Constant declaration

.equ	Def_menuitem	=$03		; startoption is default menuitem
.equ	Stack_high	=$02		; High byte of stack pointer.
.equ	Stack_low	=$57		; Low byte of stack pointer.
.equ	Tot_daypatrn	=$0B		; total number of switch day paterns defined in DATADEF.ASM
.equ	Max_menuitem	=$04		; vallue of total number of menuitems + 1.
.equ	Highwait	=$01
.equ	Lowwait		=$04
.equ	PCF8583		=0b10100000	; RTC address
.equ	PCF8583_CTRL	=0b00000000	; RTC control register
.equ	X24C02		=0b10100010	; EEPROM address
.equ	Dispclear	=$01
.equ	Dispinit	=$0C	
.equ	Eightbitinit	=$38
.equ	disp_CTRL	=0b00000000	
.equ	disp_write	=0b00000001
.equ	disp_busy	=0b00000010
.equ	disp_read	=0b00000011
.equ	disp_kontr	=$34
.equ	max_swclktimes	=$14		; Max SW times = 20
.equ	max_swchannels	=$0F		; Max channel = 16
.equ	TcountH		=$00
.equ	TcountL		=$00
.equ	Timer0		=$80
.equ	Timer1H		=$34
.equ	Timer1L		=$D3
.equ	Disp_refresh	=$FF
.equ	Temp_comp	=$04
.equ	maxday_select	=$0A
.equ	SW_arrayH_SRAM	=$00
.equ	SW_arrayL_SRAM	=$B0
.equ	SW_E2_I2C	=$80

.equ	plus_key	=$01		
.equ	min_key		=$02
.equ	Select_key	=$04
.equ	return_key	=$08

.equ	PCF8574_SWL	=0b01000000	; Base address for PCF8574
.equ	PCF8574_SWH	=0b01110000	; Base address for PCF8574A


.include	"I2Cfunc.asm"
.include	"menu.asm"
.include	"divfunc.asm
.include	"8583set.asm"
.include	"SWklok.asm"

;***************************************************************************
;
; Interrupt routine when TIMER 0 Overflow occurs
;
;***************************************************************************

TIM0_OVF:

	push	Ax				; Save Ax and Bx register to stack

	ldi	Ax, Timer0			; load TCNT0 reg. of timer 0 with init value
	out	TCNT0, Ax

	pop	Ax				; Restore Ax register value

	reti

;***************************************************************************
;
; Interrupt routine when TIMER 1 Overflow occurs
;
;***************************************************************************

TIM1_OVF:

	ldi	Disp_refresh, $FF		; Load disp_refresh register with $FF to jump
						; out of 0.9 Sec delay lus.
	push	Ax				; and save Ax and Bx register to stack.
	push	Bx

	ldi	Ax, Timer1H			; load timer counter register with $CA80
	ldi	Bx, Timer1L			; for 0.9 sec INT generation
	out	TCNT1H, Ax
	out	TCNT1L, Bx

	pop	Bx				; restore org. value's Ax and Bx from stack
	pop	Ax

	reti

RESET:
;***************************************************************************
;
; Set all I/O ports of the microcontroller to the proper vallue's
;
;***************************************************************************

	ldi	Ax, Stack_low		; Init Stack pointer. Set to highest
	out	spl, Ax			; SRAM location ( 025F ) for 8515 without ext. SRAM
	ldi	Ax, Stack_high
	out	sph, Ax

	ldi	Ax ,0b11110000		; Set PortB[7:4] to output and PortB
	out	DDRB, Ax		; [3:0] to input
					
	ldi	Ax, 0b10111111		; Set PortD to output except for bit 6. Wil be used
	out	DDRD, Ax		; as I2C data input line

	ldi	Ax, 0b11111111		; Set Port A and PortC to output.
	out	DDRA, Ax		; Used are display data bus
	out	DDRC, Ax		; used as display control signal bus.

	ldi	Ax, $00			; set all output Port's to "0" level
	out	PortA, Ax
	out	PortB, Ax 
	out	PortC, Ax
	out	PortD, Ax

	ldi	Ax, 23			; Set UBRR to 9600 Baud using 3.6864 MHz clock
	out	UBRR, Ax		; Serial port used to send switch off/on data
	ldi	Ax, 0b00001000		; to terminal
	out	UCR, Ax			; Enable transmitter

;*********************************************************************
;	
; Init Timer 0 
;
;*********************************************************************

	ldi	Ax, $02			; Set timer0 prescaler to CK/8
	out	TCCR0, Ax

	ldi	Ax, Timer0		; and set timer0 value to initvalue
	out	TCNT0, Ax 

;*********************************************************************	
;
; Init Timer 1 
; 
;*********************************************************************

	ldi	Ax, $03
	out	TCCR1B, Ax		; Set timer 1 prescaler to CK/64
	ldi	Ax, $00
	out	TCCR1A, Ax		; Disable PWM mode and I/O Pin connections

	ldi	Ax, Timer1H		; load timer counter register with $CA80
	ldi	Bx, Timer1L		; for 0.9 sec INT generation
	out	TCNT1H, Ax
	out	TCNT1L, Bx

	ldi	Ax, 0b10000000		; Enable timer 1 overflow int. and	
	out	TIMSK, Ax		; disable timer 0 overflow int

	sei				; Enable global INT.

	ldi	I2Cstatreg, $00		; load I2C status register with zero

	rcall	I2Cstop			; generate I2C stop function
	rcall	delayfunc		; delay
	rcall	initI2Cbus		; initializa I2C init function
	rcall	delayfunc		; delay

;***************************************************************************
;
;* Initialise LCD display
;
;***************************************************************************

	ldi	Ax, Dispinit		; Set display cursor function
	ldi	Bx, disp_CTRL		; and load dipslay control mode.
	rcall	disp_lcd		; init display via I/O port.

	ldi	Ax, Eightbitinit	; Set display in 8-Bit data mode
	rcall	disp_lcd		; init display via I/O port'

;***************************************************************************
;
;* Init PCF8583 RTC clock chip
;
;***************************************************************************

	rcall	I2Cstart		; I2C start function
	ldi	Ax, PCF8583		; select I2C device clock device
	rcall	I2Csend			; send adres to bus
	
	sbrs	Ax, 0			; check next adres is there is no acknowledge
	rcall	no_I2Cresponce

	ldi	Ax, $00			; Goto register 0
	rcall	I2Csend

	sbrs	Ax, 0
	rcall	no_I2Cresponce
	
	ldi	Ax, PCF8583_CTRL	; initialize clock chip
	rcall	I2Csend

	sbrs	Ax, 0
	rcall	no_I2Cresponce

	rcall	I2Cstop

;***************************************************************************
;
; Create array with vallid SW switch times starting at SRAM address $00B0
;
; Definition : $00B0 = Number of vallid switch times
;              $00B1 = Hour (time 1)
;	       $00B2 = minutes ( time 1 )
;	       $00B3 = days ( times 1 )
;	       $00B4 = temp ( time 1 )
;	       $00B5 = Hour ( time 2 )
;              cont ..........
;
;***************************************************************************

	rcall	SWswitchtime		; Create array with vallid schalekklok switch times
					; read from I2C EEPROM
	ldi	Ax, SW_arrayL_SRAM
	ldi	Bx, SW_arrayH_SRAM				
	rcall	SendSWlist		; Send SW time list to RS-232 port.					
	rcall	SWmod_press 		; Set bit's of all modules available
					; on I2C bus				
	rcall	ResetSWports		; set alle available Switch ports to off.
					
start_loop:

	ldi	Ax, Dispclear		; Set display cursor function
	ldi	Bx, disp_CTRL
	rcall	disp_lcd

;***************************************************************************
;
; check for any keyboard activity.....
;
;***************************************************************************

display_check:

	in	Ax, PinB		; Read input key's
	nop
	nop
	andi	Ax, $0F			; clear bit 4 to 7

	cpi	Ax, select_key		; check if "select" key is pressed
	brne	nokeypressed		; skip jump is Z-bit is clear
	rcall	Beep			; generate beep on speaker
	rjmp	menu			; Jump to start menu-routine

nokeypressed:
	cpi	Disp_refresh, $00	; Check for end of 0,9 sec delay created by timer 0
	breq	display_check		; if exp. then check for switch time reached

;***************************************************************************
;
; Check for channel status changes 
;
;***************************************************************************

	ldi	Ax, PCF8583		; Load I2C RTC address into Ax reg.
	ldi	Bx, $02			; Load "second" register number into Bx
	rcall	I2C_Regread		; and read in from the RTC chip
	
	cpi	Ax, $00			; check for zero second's
	brne	no_checkSWstat		; if not so, than then no channel check
					; TO PREVENT CHANNEL CHANGE FOR THE WHOLE MINUTE.
	rcall	check_SWstat		; check if any timer setting is set to
					; current RTC time for channel status chenge.

readsec_again:
	ldi	Ax, PCF8583		; Load I2C RTC address into Ax reg.
	ldi	Bx, $02			; Load "second" register number into Bx
	rcall	I2C_Regread		; and read in from the RTC chip
	
	cpi	Ax, $00			; check for zero second's
	breq	readsec_again		; if so, than wait for no zero sec
					; WAITS FOR SECOND "00" IS OVER
no_checkSWstat:
					
;***************************************************************************
;
; Start display time from the 8583 RTC device
;
;***************************************************************************

Readtime:

	ldi	Ax, low(Time)
	ldi	Bx, high(time)		
	ldi	Cx, $00			; Load display position "Tijd" string into Cx register
	rcall	Str2disp		; Print it via I/O

	ldi	Cx, $02			; load 8583 time hour register number into Cx
;	ldi	Bx, $0F			; Load display position for printing in Bx
	ldi	Bx, $0E
startread:

	ldi	Ax, PCF8583		; load I2C adres into Ax

	push	Bx			; save display position number onto stack
	mov	Bx, Cx			; load register select into Bx

	rcall	I2C_regread		; Load register vallue from RTC into Ax register
	pop	Bx			; restore display position from stack
	inc	Cx			; increase RTC register pointer to next time register
					; NOTE :	if Cx = 02 > seconds will be read
					; 		if Cx = 03 > minutes will be read
					; 		if Cx = 04 > hours will be read.

;*************************************
; start to display the found vallue
;*************************************

	push	Cx			; Save register address to stack

	mov	Cx, Bx			; Load display position into Cx reg
	mov	Bx, Ax			; move read register value into Bx reg
	mov	Ax, Cx			; move display position into Ax
	rcall	disp_DECval


	pop	Cx
	cpi	Cx, $05			; check Cx value for end of time read from RTC 
	breq	No_time			; register $05 contains no time information anymore !

	subi	Ax, $02
	ldi	Bx, 0b00111010		; load ":" charakter on display databus
	rcall	dispCHR			; and display it.
	
	mov	Bx, Ax
	subi	Bx, $02

	rjmp	Startread		; read next time tegister from RTC.

No_time:

;**************************************************
;
; Display Day of week text on the LC-Display
;
;**************************************************

	ldi	Ax, PCF8583		; load I2C adres into Ax
	ldi	Bx, $06			; load Day of Week reg. vallue

	rcall	I2C_regread		; Load register vallue from RTC into Ax register

	andi	Ax, 0b11100000		; Remove month information from Ax
	swap	Ax			; swap byte's and multiply value by two
					; for calculating day of week address pointer
 	mov	Cx, Ax			; Save day information in Cx as offset
	
	ldi	Ax, low(dagen)		; load data pointer of day "string"
	ldi	Bx, high(dagen)		; into Ax and Bx reg. and
	rcall	calcpointer		; calculate proper memory pointer
	
	mov	Z_low, Ax		;and move address location in Z-register
	mov	Z_high, Bx
	
	ldi	Ax, $40			; Load Ax with new display location
	lpm				; load 1st CHR into Bx register	
	mov	Bx, R0			; via R0
	rcall	dispCHR			; and display it
	
	adiw	Z_low, $01		; increase memory pointer by one
	lpm				; and load 2nd CHR into Bx
	mov	Bx, R0			; via R0
	inc	Ax			; increase display location by one
	rcall	DispCHR			; and display CHR

;***************************************************
;
; Display Day off month number on the display
;
;***************************************************
	
	ldi	Ax, PCF8583		; load I2C adres into Ax
	ldi	Bx, $05			; load Day off Month reg. vallue
	rcall	I2C_regread		; Load register vallue from RTC into Ax register

	andi	Ax, 0b00111111		; clear bit [7:6]
	mov	Bx, Ax			; and move it to Bx reg.
	ldi	Ax, $43			; load display position into Ax reg
	rcall	Disp_DECval		; and display the day of month
	
;**************************************************
;
; Set  Month Text on the display
;
;**************************************************

	ldi	Ax, PCF8583		; load I2C adres into Ax
	ldi	Bx, $06			; load Day of Week reg. vallue

	rcall	I2C_regread		; Load register vallue from RTC into Ax register
	andi	Ax, 0b00011111		; Extract month information from Ax
	rcall	DEC2HEX			; convert DEC value to HEX value
	dec	Ax			; 
	lsl	Ax			; and multiply by four
	lsl	Ax
	mov	Cx, Ax			; and load it into Cx as address offset
	ldi	Ax, low(Maanden)	; Load month txt pointer into Ax and Bx
	ldi	Bx, high(Maanden)
	rcall	calcpointer
	
	mov	Z_low, Ax
	mov	Z_high, Bx
	
	ldi	Cx, $04			; Load Cx with Txt length
	ldi	Ax, $46			; Load display position into Ax
	
Next_MonthCHR:	
	lpm				; Get CHR to display
	mov	Bx, R0			; and move it into Bx reg
	rcall	DispCHR			; and display it
	adiw	Z_low, $01		; increase Text pointer by one
	inc	Ax			; increase display position by one
	dec	Cx			; decrease CHR counter by one
	cpi	Cx, $00			; check for ned of string
	brne	Next_MonthCHR

;*************************************************
;
; Set "20" on the display 
;
;*************************************************
	
	ldi	Ax, $4B			; Load Ax with display position
	ldi	Bx, $32			; Load Bx with "2"
	rcall	DispCHR
	
	inc	Ax			; go to next display position
	ldi	Bx, $30			; Load Bx with "0"
	rcall	DispCHR	

;*************************************************
;
; Display Number of Year on the screen.
;
;**************************************************
	
	ldi	Ax, PCF8583
	ldi	Bx, $FF			; read year information from RTC SRAM location
	rcall	I2C_regread		; read register Bx from device Ax
	push	Ax			; Save HEX year value onto stack
	rcall	HEX2DEC	

	mov	Bx, Ax
	ldi	Ax, $4D
	rcall	disp_DECval
	
;***********************************************************
;
; Check for Year increase in SRAM RTC clock
;
;***********************************************************

	ldi	Ax, PCF8583
	ldi	Bx, $05			; read year information from RTC SRAM location
	rcall	I2C_regread		; read register Bx from device Ax
	
	swap	Ax			; Move Year information to Bit[0:1]
	lsr	Ax
	lsr	Ax
	andi	Ax, 0b00000011		; Clear Bit[2:7]
	
	pop	Cx			; Remove HEX year info from stack
	mov	Bx, Cx			; Save HEX year value in Bx reg.
	andi	Cx, 0b00000011		; clear bit[2:7]
	cp	Ax, Cx			; check if year is stil the same
	breq	no_yearinc
	
	inc	Bx			; Increase HEX year info by one
	sbrc	Bx, 6
	clr	Bx
	
	mov	Cx, Bx			; Save new year number into Cx reg
	ldi	Ax, PCF8583		; Loead Ax with RTC I2C address
	ldi	Bx, $FF			; Load Bx reg. with RTC SRAM location
	rcall	I2C_regstore		; Store new year value into RTC SRAM location

no_yearinc:
	rjmp	display_check		; jump back to start of loop


;***************************************************************************
;
; Delayfunction to set the I2C speed. Adjustable for different CPU speed.
;	Used registers : Ax = temp var, only vallid in this function
;			 Bx = temp var, only vallid in this function
;
;	Return : None
;
;***************************************************************************
	
delayfunc :

	push	Ax
	push	Bx

	ldi	Bx, Highwait			; Set registers for proper delay
	ldi	Ax, Lowwait

No_zero:
	dec	Ax				; decrease low byte and check for zero
	brne	No_zero				; if not zero then subtrack again

	dec	Bx				; decrease high byte and check for zero
	breq	End_delay			; return from subroutine is highbyte is zero

	ldi	Ax, Lowwait			; reload lowbyte nd jump to No_zero
	rjmp	No_zero

End_delay:

	pop	Bx
	pop	Ax

	ret					; return from subroutine

;***************************************************************************
;
; Delayfunction to set the I2C speed. Adjustable for different CPU speed.
;	Used registers : Ax = temp var, only vallid in this function
;			 Bx = temp var, only vallid in this function
;
;	Return : None
;
;***************************************************************************
	
delay_5mS :

	push	Ax
	push	Bx

	ldi	Bx, $80				; Set registers for proper delay
	ldi	Ax, $00

No_zero_5mS:
	dec	Ax				; decrease low byte and check for zero
	brne	No_zero_5mS				; if not zero then subtrack again

	dec	Bx				; decrease high byte and check for zero
	breq	End_delay_5mS			; return from subroutine is highbyte is zero

	ldi	Ax, $00				; reload lowbyte nd jump to No_zero
	rjmp	No_zero_5mS

End_delay_5mS:

	pop	Bx
	pop	Ax

	ret					; return from subroutine


;***************************************************************************
;
; Display a character to LCD display
;
;	Used registers : accureg
;			 Ax = display position register
;			 Bx = Character to display
;
;	Return : None
;
;***************************************************************************

dispCHR:

	push	Ax
	push	Bx
	
	ori	Ax, $80
	ldi	Bx, disp_CTRL
	rcall	disp_LCD

	pop	Bx
	mov	Ax, Bx
	ldi	Bx, disp_write
	rcall	disp_LCD
	
	pop	Ax

	ret	

;***************************************************************************
;
; check for no key pressed
;
;***************************************************************************

key_released:

	push	Ax			; Save Reg. Ax and Bx to stack
	push	Bx
	
	ldi	Ax, $04			; Load ax with cycle counter value
	
check_key_again:
	dec	Ax
	cpi	Ax, $00			; check for 3 times no key pressed
	breq	end_key_released	; Jump out if so.


	rcall	delay_5ms	
	in	Bx, PinB		; Read key's
	andi	Bx, $0F			; Clear high nibble from data

	cpi	Bx, $00			; check for no key pressed
	breq	check_key_again		; jump if no key pressed
	
	ldi	Ax, $04
	rjmp	check_key_again

end_key_released:

	pop	Bx
	pop	Ax

	ret

;***************************************************************************
;
; Display a string to the display until the data-byte read is $00 using data
; defenition id EEPROM.
;
;	Used registers : Ax = low byte address data pointer
;			 Bx = high byte of address data pointer
;			 Cx = startposition display
;
;***************************************************************************

str2disp:

	out	EEARL, Ax		; write EEPROM addres to read in EEPROM addres 
	out	EEARH, Bx

start_displaySTR:

	mov	Ax, Cx			; load display position into Ax reg.
	ori	Ax, $80			; Set MSB in Ax reg. ( See display data sheet )
	ldi	Bx, disp_CTRL		; load display control reg. prot. into Bx
	rcall	disp_LCD		; go display it.
	rcall	disp_ready		; Wait until display's busy flag is clear
			
	ldi	Bx, disp_write		; Load display data write prot. in Bx

Str_loop:

	sbi	EECR, EERE		; Set EEPROM Read start bit	
	
check_EERE:
	in 	Ax, EECR		; load EECR status in Ax reg.
	sbrc	Ax, 0			; check if EEPROM read bit is cleared by hardware
	rjmp	check_EERE		; if not, then wait until reset
	
	in	Ax, EEDR 		; Load read EEPROM data into Ax reg.
	cpi	Ax, $00			; and check for "$00" end of data
	breq	end_str2disp		; jump to end of routine if so.
	
	rcall	disp_LCD		; display read ASCII value onto display
	rcall	disp_ready
	
	in	Ax, EEARL		; load Ax with current EERPOM address
	inc	Ax			; Inc Address by one
	out	EEARL, Ax		; Store new address into EEPROM reg.

	cpi	Ax, $00
	brne	no_pageinc
	
	in	Ax, EEARH
	inc	Ax
	out	EEARH, Ax

	
no_pageinc:	
	inc	Cx			; inc. display position by one

	rjmp	Start_displaySTR	; Jump to start of routine.

end_str2disp:

	ret
	
;***************************************************************************
;
; Routine to control the LCD display
;	Ax = Data to display
;	Bx = 0b00000000	= Display control register mode
;	     0b00000001 = CG RAM/DD RAM data write
;	     0b00000010 = Busy flag / address counter read
;	     0b00000011 = CG RAM/DD RAM data read
;
;***************************************************************************

Disp_LCD:



	out	PortA, Ax		; Put data on displays data bus
	
	swap	Bx			; swap high and low nibble to get proper	
	out	PortC, Bx		; value of RS and R/W bit and output it
					; to port C.
	ori	Bx, 0b01000000		; Set E bit of display and set new value on
	out	PortC, Bx		; port C
	andi	Bx, 0b10111111		; Reset E bit of display and set new value on
	out	PortC, Bx
	
	clr	Bx
	out	PortC, Bx		; Set all display control line's low.					
	
	rcall	Disp_ready
				
	ret
	
;***************************************************************************
;
; Routine that wait's until the display is ready.
; ( To avoid inpropper display information )	
;
;***************************************************************************	

disp_ready:

	push	Ax			; Save registers Ax and Bx to stack
	push	Bx

	ldi	Ax, 0b00000000		; Set port A to input
	out	DDRA, Ax

not_ready:
	
	ldi	Ax, disp_busy		; Load Read Busy sequence into Ax
	swap	Ax			; Swap Byte to get proper bit mask
	out	PortC, Ax		; Output to display control lines
	
	ori	Ax, 0b01000000		; Set E bit high
	out	PortC, Ax		; And output new bit mask to port C
	nop				; wait one clock cycle
	in	Bx, PinA		; Read display busy flag
	andi	Ax, 0b10111111		; Reset E bit
	out	PortC, Ax		; and output new bit mask to PortC
	
	sbrc	Bx, 7			; Check it display is ready ( Bit 7 = 0 )
	rjmp	not_ready
	

	ldi	Ax, 0b11111111		; Set port A to output
	out	DDRA, Ax

	pop	Bx
	pop	Ax
	
	ret

;***************************************************************************
;
; Routine voor het uitlezen van de toetsen aangesloten op PORTB[0:3]
;	
;	Ax = return waarde van de ingedrukte toets
;
;***************************************************************************


Input_keyread:

	in	Ax, PinB		; Read input key's
	andi	Ax, $0F
	
	cpi	Ax, return_key		; Check for Return key pressen
	breq	end_keyreadsub		

	cpi	Ax, select_key		; check if Select key is pressed
	breq	end_keyreadsub

	cpi	Ax, plus_key		; Check if "+" key is pressed
	breq	end_keyreadsub

	cpi	Ax, min_key			; Check if "-" key is pressed
	breq	end_keyreadsub

	rjmp	Input_keyread		; read key's again if no key is pressed

end_keyreadsub:

	rcall	Beep

	ret

;***************************************************************************
;
; Check for changes in SW-switch modules.
;
;***************************************************************************

Check_SWstat:

	push	Ax			; Save register Ax, Bx and Cx to stack
	push	Bx
	push	Cx
	push	Z_low
	push	Z_high

	clr	Cx			; Clear Cx register.

checkagain:

	ldi	Ax, SW_arrayL_SRAM	; Load Ax end Bx with start address of
	ldi	Bx, SW_arrayH_SRAM	; array in SRAM defined is variables

	rcall	check_change		; check for wanted channel change time in array.

	cpi	Ax, $00			; If return value is NOT equel to zero, there is
	brne	SWchange		; an channel status change. If not then jump out.
	rjmp	noSWchange

SWchange:
	push	Bx			; save timer loop number
	push	Ax			; save channel number/stat information on stack

	rcall	SendTime		; Send current time on the RS-232 port
	pop	Ax			; reload Ax with number/stat info
	push	Ax
	rcall	Send_chanstat		; Send channel number and stata info
					; to the RS-232 port.
	ldi	Ax, $0D			; send linefeed to RS-232 port
	rcall	sendCHR
	ldi	Ax, $0A			; send CR to RS-232 port
	rcall	sendCHR
	
	pop	Ax			; reload channel/state info from stack
	mov	Bx, Ax
	
	andi	Ax, $F0			; remove channel stat. information
	swap	Ax
	andi	Bx, $0F
	rcall	activate_channel

	pop	Bx			; reload timer vallue
	cpi	Bx, $00			; check for end of array check reached.
	mov	Cx, Bx			; if not, move vallue to Cx to become preset val
	breq	noSWchange
	rjmp	checkagain		; and go to check again

noSWchange:

	pop	Z_high
	pop	Z_low
	pop	Cx
	pop	Bx
	pop	Ax

	ret
	
;**************************************************************************
;
; Routine to check for new SW clock settings
;
;	Ax	= Low byte start addres in SRAM of array
;	Bx	= High byte start address in SRAM of array
;	Cx	= Preset for vallid time counter
;
;	Return:
;	Ax	= new vallue of settings (SW channel stat/numb.)
;	Bx	= number of time setting vallid for changecheck
;
;**************************************************************************

check_change:

	mov	Z_low, Ax		; Load Z_register with start array in SRAM. 
	mov	Z_high, Bx		; as defined in Ax (low byte) and Bx (high byte)
	push	Ax			; Save org SRAM start addres to stack
	push	Bx

	mov	Bx, Cx			; Move counter preset to Bx register

	ld	Cx, Z+			; Load number of vallid clock settings into Cx
					; and set SRAM pointer to next location.
	sub	Cx, Bx			; Adjust counter with Preset
	lsl	Bx			;
	lsl	Bx			; Multiply Bx contens by 4 for calculating SRAM offset
	add	Z_low,Bx		; compensate Z_register pointer start address.

chk_nexttime:
	cpi	Cx, $00			; check for last timer setting.
	brne	Notimeend			 
	rjmp	end_switchchk		; jump to end of subroutine if last time is checked

Notimeend:

	ldi	Ax, PCF8583		; Load I2C RTCclock address in Ax register.
	ldi	Bx, $04			; Load Hour reg. number into Bc register.
	rcall	I2C_regread		; Load RTC current hour vallue into Ax

	ld	Bx, Z+			; Load Bx with Clock hour setting from SRAM
	cp	Ax, Bx			; and set increase SRAM pointer. compare hour settings
	breq	chk_switchmin		; if equal then check for minute setting
	subi	Z_low, $FD		; Set Z-register to next Hour SRAM address ( by adding 2 )
	dec	Cx			; decrease total timesetting counter. 
	rjmp	chk_nexttime		; Jump to check next timer setting.

chk_switchmin:
	ldi	Ax, PCF8583		; Load I2C RTCclock address in Ax register.
	ldi	Bx, $03			; Load minute reg. number in Bx register
	rcall	I2C_regread		; Load RTC current minutes vallue into Ax.

	ld	Bx, Z+			; Load Bx with CV-clock minute setting from SRAM and inc SRAM pointer
	cp	Ax, Bx			; if equel then check for switch day setting
	breq	chk_switchday
	subi	Z_low, $FE		; Set Z_register to next hour SRAM address
	dec	Cx			; decrease total timesetting counter.
	rjmp	chk_nexttime		; Jump to check next times setting


chk_switchday:

	ldi	Ax, PCF8583		; Load I2C device number
	ldi	Bx, $06			; Load RTC Weekday/Month register number
	rcall	I2C_regread		; Load Ax with RTC current day
	andi	Ax, $E0			; Remove month information
	swap	Ax			; and swap byte to get a number from 0 to 6
	lsr	Ax			; Ax contains RTC day of week information
	ldi	Bx, $01			; Set Bx LSB

calc_day:
	cpi	Ax, $00			; shift Bx to set proper day bit to check
	breq	end_calc_day		; with SRAM day setting
	lsl	Bx			; if Ax is NOT 0 then shift Bx to left without carry
	dec	Ax			
	rjmp	calc_day		; Bx contains bitset for current day

end_calc_day:
;	mov	Ax, Bx			; move proper day bit patern into Ax
;	mov	Bx, Ax
	ld	Ax, Z+			; Load wanted day information from SRAM
	push	Z_low
	push	Z_high
	push	Cx			; Save register Cx and Bx to stack
	push	Bx
	mov	Cx, Ax			; mov	dayinfo to Cx as address offset
	ldi	Ax, low(dayinfo)	; Load pointer into Ax and Bx
	ldi	Bx, high(dayinfo)
	rcall	calcpointer		; calculate data memory position
	mov	Z_low, Ax		; and move pointer into Z-reg.
	mov	Z_high, Bx
	pop	Bx			; reload wanted day-bitstream into Bx 
	pop	Cx
	
	lpm				; load day-bit pattern from Programm memory
	mov	Ax, R0			; into Ax through R0
	pop	Z_high			; restore Z vallue
	pop	Z_low
	and	Ax, Bx			; logical AND to check if proper day bit is selected

	breq	no_day_select		; jump is proper day bit is NOT set

	ld	Ax, Z			; load wanted channel number/state infomation into Ax again
	rjmp	time_vallid

no_day_select:
	subi	Z_low, $FF		; set SRAM pointer to next Hour vallue
	dec	Cx			; decrease total timer check counter
	rjmp	chk_nexttime		; goto start check next setting

end_switchchk:

	clr	Ax			; Clear AX to indicate no vallid time found.

time_vallid:


	pop	Z_high			; Load Z-register with startaddress SRAM and load
	pop	Z_low			; Bx with total vallid time vallue
	ld	Bx, Z
	dec	Cx
	sub	Bx, Cx			; subtrack current loop vallue of timer check
	
	ret
	
;***************************************************************************
;
; Create SW time array in SRAM starting at address defined in SW_arrayH_SRAM
; and SW_arrayL_SRAM.
;
;***************************************************************************
;
; Startaddres in EEPROM is : $80 = hour setting
;			     $81 = minute setting
; 			     $82 = Day pattern
;			     $83 = on off state / channel
;			     $84 = 2-comp from above vallue's
; Array is taking space up to addres $A7
;
;***************************************************************************

SWswitchtime:

	push	Ax			; Save Ax, Bx and Cx register to stack.
	push	Bx
	push	Cx


	ldi	Ax, SW_E2_I2C		; load I2C EEPROM start address of time table in Ax
	ldi	Bx, SW_arrayH_SRAM	; load SRAM address in Bx ( high byte ) and Cx 
	ldi	Cx, SW_arrayL_SRAM	; ( low byte )

	rcall	createArray		; Go create Array in SRAM

	pop	Cx			; Restore org vallue's of Cx, Bx and Ax register
	pop	Bx
	pop	Ax

	ret

;********************************************************************
;
; Routine to create an Array in SRAM as :
;
;	Ax	= I2C EEPROM startaddres for time information
;	Bx	= High byte start addres in SRAM of array
;	Cx	= Low byte start address in SRAM of array
;
;********************************************************************

createArray:

	push	Z_low			; Save current Z-pointer to Stack
	push	Z_high
	push	Cx			; save Cx to stack.

	mov	Y_low, Cx		; and load it into Y-reg(low)
	mov	Y_high, Bx		; and load it into Y-reg (high)
	adiw	Y_low, $01		; Increase Y pointer by one.
	mov	Bx, Ax			; and move I2C EEPROM address to
					; read from into Bx reg.

	ldi	Cx, max_SWclktimes	; Load Cx with max SW timer setting

SWnext_time:

	push	Cx			; save Cx to stack and load it with the
	ldi	Cx, $04			; number of registers to read from I2C devie

SWload_lus:
	dec	Cx
	ldi	Ax, X24C02		; load I2C device from location dev. in Bx
	rcall	I2C_regread		; Read from I2C device
	st	Y+, Ax			; Save in SRAM in increase SRAM pointer by one. 	
 
	inc	Bx			; Increase I2C reg number 1.
	cpi	Cx, $00			; check for read of last register.
	brne	SWLoad_lus		; goto read next register.

	clr	Ax			; clear Ax register.
	push	Bx			; save I2C device Reg. nr. on stack.
	ldi	Bx, $04			; load Bx with total byte's to read from SRAM.

SW2comp_lus:
	dec	Bx			; Calculate the 2 complement from the byte's
	ld	Cx, -Y			; Load Cx with SRAM location and dec. SRAM pointer by 1
	add	Ax, Cx			; add result to Ax
	cpi	Bx, $00
	brne	SW2comp_lus		; check for all byte readed
	com	Ax
	subi	Ax, $ff			; calculate 2-comp from Ax
					; Ax reg. contains 2comp value of previous read four
	pop	Bx			; bytes. Reload Bx with I2C reg. number
	mov	Cx, Ax			; Move calculated 2COMP val. into Cx reg.
	ldi	Ax, X24C02
	rcall	I2C_regread		; Load 2-comp val location in I2C device
	inc	Bx			; increase Bx to set proper EERPOM read addres
	cp	Cx, Ax			; check if vallue in I2C device is the 
	brne	SWnotvallid_2comp	; calculated 2comp.

	subi	Y_low, $FC		; Set Y-pointer to new SRAM start addres
					; if calculate and read vallue are equal.

SWnotvallid_2comp:
	pop	Cx
	dec	Cx
	cpi	Cx, $00			; check for for all posible SW-timer setting ( 10 ).
	brne	SWnext_time

	mov	Ax, Y_low		; Load Ax with low byte Y-pointer and
	dec	Ax
	ldi	Bx, SW_arrayL_SRAM
	sub	Ax, Bx
;	subi	Ax, $7F			; subtrakt $7F and devide in by two by
	lsr	Ax			; shift vallue right 2 bits without carry.
	lsr	Ax			; Ax contains total found vallid SW times
	pop	Cx			; restore low byte SRAM vallue 
	mov	Y_low,Cx		; load Y-low org. vallue
	st	Y, Ax			; Store total Sw-times number into SRAM location 

	pop	Z_high			; Retore org Z-register vallues
	pop	Z_low

	ret

;************************************************************************
;
; Routine to set I/O module in the wanted state:
;
;	Ax = channel number
;	Bx = wanted state of channel ( LSB = 0 > off )
;				     ( LSB = 1 > onn )
;***********************************************************************

activate_channel:

	push	Cx			; save Cx reg. on stack
	push	Bx			; save channel status on stack

	sbrs	Ax, 3			; check for 74A module to change
	rjmp	change_lowSW
	
	andi	Ax, 0b00000111		; Remove bit[3]
	lsl	Ax			; multiply SW number by two
	ldi	Bx, PCF8574_SWH		; Load Bx with basic I2C 8574A address
	add	Ax, Bx			; add number of SW to I2C address
	rjmp	changestate
	
change_lowSW:

	lsl	Ax
	ldi	Bx, PCF8574_SWL
	add	Ax, Bx
		
changestate:	
	
	pop	Bx			; reload channel number/stat information
	ldi	Cx, $FF			; Load Cx with all bits high
	sbrs	Bx, 0			; check if LSB Bx id "0", if so
	clr	Cx			; then clear Cx register
	mov	Bx, Cx			; load stat information from Cx into Bx

	rcall	I2Cstart		; send I2C start command on I2C bus
	rcall	I2Csend			; send I2C device address on the bus

	sbrs	Ax, 0			; check for aknowledge from device
	rjmp	norespI2C

	mov	Ax, Bx
	rcall	I2Csend
	
	sbrs	Ax, 0
	rcall	no_I2Cresponce		; if no responce the I2C error on display

	rcall	I2Cstop	

norespI2C:

	pop	Cx
	
	ret

;.include	"data_ned.asm"
.include	"data_eng.asm"