$include (C8051F98x.inc)      
pressTsch	EQU	0x3F			; press threshold
releaseTsch EQU	0xFC			; release threschold 

alarmB	EQU	7		
setupB	EQU	6
snoozeB	EQU	5
dimB		EQU	4
alarmBold	EQU	3
setupBold	EQU	2
snoozeBold	EQU	1
dimBold	EQU	0
ampm		EQU	5

AlarmRings	EQU	Status.4		; 1=alarm is ringing
Backlight	EQU	Status.3		; on/off
Power		EQU	Status.2		; high/low power mode (0=low power)
Temp		EQU	Status.1		; displaying temp or date in the 2nd line of LCD
Alarm		EQU	Status.0		; setting the wake-up alarm

	DSEG 	AT 0x20			; data segment in direct addressable space
bStati: 	DS 2				; buttons stati (0123 0123 | new old)
Status: 	DS 1				; Clock State and Display Booleans (Not Idle|set alarm/time|3 bits sub-state|backlight|Date/Temp|Alarm
hour:		DS 1				; storage for hours
alhour:	DS 1				; storage for alarm hours

	ORG	0x30
min: 		DS 1				; storage for minutes
almin:	DS 1				; storage for alarm minutes
day:		DS 1				; stirage for date
month:	DS 1				; storage for month
year:		DS 1				; storage for year
state:	DS 1				; 0: idling, 1: setup hrs, 2: setup min, 3: am/pm, 4: set date
						; 5: set month, 6: set year, 7: set aralm hrs, 8: set alarm min
						; 10: set alarm am/pm

shiftAl: 	DS 1				; alarm/ screen on button shift reg
shiftSu: 	DS 1				; setup button shift reg
shiftSz: 	DS 1				; snooze button shift reg
shiftDi: 	DS 1				; dim button shift reg				

wupCnt:	DS 1				; wake-up counter

	CSEG 	AT 0				; interrupt vectors
	ljmp 	Main  			; reset

	ORG	0x0B				; Timer0 vector
	reti

	ORG	0x83				; minute interrupt vector
;	ljmp	Minute_Interupt
;	reti
	lcall	Minute_Interrupt
	reti

	ORG	0x2B				; Timer2 vector
	clr	TMR2CN.7			; clear interrupt flag
	xrl	P1SKIP, #0x20		; complement CPA0 output
	reti	

	ORG	0xB3				; code section right after the int. vectors
	USING 0          			; specify register bank 

main:       
	anl   PCA0MD, #NOT(0x40) 	; disable the WDT
	mov	SP, #0x7F			; setup stack in indirect storage area

	acall Clock_Setup
	acall	Ports_Setup
	acall	Timers_Setup
	acall	ADC_Setup
	acall	I2C_Setup
	acall RTC_Setup
	acall	SmaRTClock_Setup
		
	setb	IE.7				; enable interrupts
	acall	Init_LCD

	mov	Status, #0x00		; Backlight off, Date display, Low power mode
	mov	A, #0xFF			; initialize button registers
	mov	shiftAl, A
	mov	shiftSu, A 
	mov 	shiftSz, A
	mov	shiftDi, A
	mov	bStati, A
	mov	state, #0			; init FSM state
	mov	wupCnt, #1

	acall Get_Time_date
	acall Display_Time
	acall Display_Date

loop:						; main loop
	orl	PCON, #0x01			; set CPU idle mode
	mov	PCON, PCON			; dummy 3-cycle instruction

	orl	bStati+1, #0xF0		; preset all buttons off
	anl	bStati,   #0x0F

	jb	P0.4, checkADC 
	clr	bStati+1.alarmB
	sjmp	processBt

checkADC:
	setb	AD0EN				; turn ADC on
	djnz	wupCnt, checkBut 
	mov	wupCnt, #101		; restore counter

	mov  	ADC0MX, #0x0B		; set input P1.3 for the power line	
	mov	A, #4				; 10 usec delay
	djnz	ACC, $
	setb	AD0BUSY			; request conversion
	nop
	nop		
	jb	AD0BUSY, $			; wait for conversion end

	mov	A, ADC0H			; get external voltage value
	subb	A, #85
	mov	F1, C
	cpl	C
	mov	Power, C
;	acall	SetBackLight		; control back light
	jnb	F1, checkBut		; the power line is high
	call	LPM

checkBut:
	mov	ADC0MX, #0x03		; set input P0.3 for checking buttons
	setb	AD0BUSY			; request conversion
	nop
	nop		
	jb	AD0BUSY, $			; wait for conversion end
	clr	AD0EN				; turn off ADC
	mov	A, ADC0H			; get ADC value

	subb	A, #192
	jnc	dimBOn
	add	A, #64
	jc	snoozeBOn
	add	A, #64
	jc	setupBOn
	sjmp	processBt	

dimBOn:
	clr	bStati+1.dimB
	sjmp	processBt
snoozeBOn:
	clr	bStati+1.snoozeB
	sjmp	processBt
setupBOn:
	clr	bStati+1.setupB

;--------------------------------------------------------------
processBt:					; debouncing the Alarm button
	mov	C, (bStati+1).alarmB	; the btState register
	mov	A, shiftAl			; debouncing the Alarm button
	rrc	A
	mov	shiftAl, A

	clr	C
     	jnb  	bStati.alarmBold, deb0 	; old state = "up" ?	
	subb	A, #(pressTsch+1)		; YES: set C=1 if SR <= pressTsch
	mov	bStati.alarmB, C		; if so, set the button pressed flag
	sjmp	$+4
deb0:	subb	A, #releaseTsch		; C=0 if SR >= releaseTsch

	cpl	C
     	mov  	bStati.alarmBold, C 	; update button state
;--------------------------------------------------------------
	mov	C, (bStati+1).setupB	; debouncing the Setup button
	mov	A, shiftSu
	rrc	A
	mov	shiftSu, A

	clr	C
     	jnb  	bStati.setupBold, deb1 	; old state = "up" ?	
	subb	A, #(pressTsch+1)		; YES: set C=1 if SR <= pressTsch
	mov	bStati.setupB, C		; if so, set the button pressed flag
	sjmp	$+4
deb1:	subb	A, #releaseTsch		; C=0 if SR >= releaseTsch

	cpl	C
     	mov  	bStati.setupBold, C     ; update button state	
;---------------------------------------------------------------
	mov	C, (bStati+1).snoozeB	; debouncing the Snooze button
	mov	A, shiftSz
	rrc	A
	mov	shiftSz, A

	clr	C
     	jnb  	bStati.snoozeBold, deb2	; old state = "up" ?	
	subb	A, #(pressTsch+1)		; YES: set C=1 if SR <= pressTsch
	mov	bStati.snoozeB, C		; if so, set the button pressed flag
	sjmp	$+4
deb2:	subb	A, #releaseTsch		; C=0 if SR >= releaseTsch

	cpl	C
     	mov  	bStati.snoozeBold, C   	; update button state
;------------------------------------------------------------------------
	mov	C, (bStati+1).dimB	; debouncing the Dim button
	mov	A, shiftDi
	rrc	A
	mov	shiftDi, A

	clr	C
     	jnb  	bStati.dimBold, deb3   	; old state = "up" ?	
	subb	A, #(pressTsch+1)		; YES: set C=1 if SF <= pressTsch
	mov	bStati.dimB, C		; if so, set the button pressed flag
	sjmp	$+4
deb3:	subb	A, #releaseTsch		; C=0 if SR >= releaseTsch

	cpl	C
     	mov  	bStati.dimBold, C       ; update button state

	call	FSM
	ajmp	loop
	
;-------------------PROCEDURES-------------------------------------------
Clock_Setup:
	mov  	CLKSEL, #0x44		; LPO with 1:16 divider
	ret

;------------------------------------------------------------------------
Ports_Setup:
	mov  	P0MDIN,    	#0xF7		; analog operation on P0.3
	mov  	P1MDIN,    	#0xF7		; analog operation on P1.3
	mov  	P0MDOUT,   	#0xE0		; OS mode on P0[4:0]
	mov  	P1MDOUT,   	#0xF7		; input on P1.3
	mov  	P0SKIP,    	#0xFC		; skip P0.0 and P0.1 for SMB
	mov  	P1SKIP,    	#0xFF		; skip P0.5 for PCA0
	mov  	XBR0,      	#0x04		; enable SMB
	mov  	XBR1,      	#0x01		; enable CEX0
	mov  	XBR2,      	#0x40		; enable Xbar
	mov	P0,	     	#0x1F
	mov	P1,	     	#0x98
	mov	P2,	     	#0x80
	mov 	A,	     	#0x04
	mov	P0MASK,    	A
	mov	P0MAT,	A
	mov	EIE2,		#0x02
	ret

;------------------------------------------------------------------------
Timers_Setup:
	mov	TMOD,  #0x22		; mode_2 for Timer0 and Timer1
	mov	CKCON, #0x0A		; use SYSCLK for Timers1,2 and 1:48 Timer0
	
	mov 	TH0,	 #0
	mov	TH1,   #250			; bit rate for I2C
	mov	TCON,  #0x10		; enable Timer0

	mov	TMR2RLL, #0			; reload value
	mov	TMR2RLH, #0			; and select SYSCLK/12
;	mov	TMR2CN,  #0x04		; enable Timer2
	mov	IE,    #0x02		; enable Timer0 interupt
	
	mov	PCA0MD, #0x08		; set SYSCLK timebase for PCA0
	mov	PCA0CPM0, #0x46		; setup for frequency output
	mov	PCA0CPH0, #229		; generate 2.73 KHz	
	ret

;------------------------------------------------------------------------
ADC_Setup:
	mov  	ADC0MX, #0x03		; set input P0.3
	mov  	ADC0CF, #0x05		; set gain x1, 8-bit mode
	mov	ADC0AC, #0x20		; left-justify results
	mov	REF0CN, #0x08		; set VDD as reference
	ret

;------------------------------------------------------------------------
I2C_Setup:
	mov	SMB0CF, #0x01		; SMBUS clock by Timer 1 overflow
	ret

;------------------------------------------------------------------------
RTC_Setup:
	orl 	TCON, #0x40      		; enable Timer 1
	orl 	SMB0CF, #0x80           ; enable SMBUS
	acall Start_I2C
	mov 	SMB0DAT, #0xD0        	; RTC chip address + write
	acall	Write_I2C
	mov 	SMB0DAT, #0x07        	; register pointer
	acall Write_I2C
	mov 	SMB0DAT, #0x00		; setup alarm 1 (wake-up alarm)
	acall	Write_I2C
	mov 	SMB0DAT, #0x52
	acall	Write_I2C
	mov 	SMB0DAT, #0x01
	acall	Write_I2C
	mov 	SMB0DAT, #0x81
	acall	Write_I2C
	mov 	SMB0DAT, #0x80		; setup alarm 2 (every minute alarm)
	acall	Write_I2C
	mov 	SMB0DAT, #0x81
	acall	Write_I2C
	mov 	SMB0DAT, #0x81
	acall	Write_I2C
	mov 	SMB0DAT, #0x07		; set control register (osc on, blanks, enable interupts and set alarm 2 to intb
	acall	Write_I2C
	mov 	SMB0DAT, #0x00		; reset flags
	acall	Write_I2C
	acall Stop_I2C
	anl	SMB0CF, #0x7F           ; disable SMBUS
	anl 	TCON, #0xDF         	; disable Timer 1

	mov	alhour, #0x52
	mov	almin,  #0x00
	ret

;------------------------------------------------------------------------
SmaRTClock_Setup:
	mov	RTC0ADR, #(0x10 OR RTC0XCN)	
	mov	RTC0DAT, #RTC0AEN		; enable LFO
	nop
   	mov	RTC0ADR, #(0x10 OR RTC0CN)	; enable SmaRTClock
	mov	RTC0DAT, #0x80  

	mov 	RTC0ADR, #(0x10 + CAPTURE0)	; clear RTC timer
	mov 	RTC0DAT, #0				; autoincrement address
	nop						; feature does not work!
	mov 	RTC0ADR, #(0x10 + CAPTURE1)
	mov 	RTC0DAT, #0
	nop
	mov 	RTC0ADR, #(0x10 + CAPTURE2)
	mov 	RTC0DAT, #0
	nop
	mov 	RTC0ADR, #(0x10 + CAPTURE3)
	mov 	RTC0DAT, #0
	nop
	mov	RTC0ADR, #RTC0CN			; set timer value
	mov	RTC0DAT, #(0x80 OR RTC0SET) 
	nop
	mov 	RTC0ADR, #(0x80 OR RTC0CN)
	nop
	nop
	nop
	mov 	A, RTC0DAT			
	jb	ACC.1, $-8				; verify that timer is set

   	mov	RTC0ADR, #(0x10 OR RTC0CN)	; disable alarm while
   	mov	RTC0DAT, #(RTC0CN_Local AND NOT(RTC0AEN)) ; updating the regs      
   	nop
	mov 	RTC0ADR, #(0x10 OR ALARM0)	; load alarm regs       
	mov   RTC0DAT, #0
	nop
	mov 	RTC0ADR, #(0x10 OR ALARM1)
	mov	RTC0DAT, #0x40			; setup for 1 sec wake-up
	nop
	mov 	RTC0ADR, #(0x10 OR ALARM2)
	mov	RTC0DAT, #0 
	nop
	mov 	RTC0ADR, #(0x10 OR ALARM3)
	mov	RTC0DAT, #0
	nop
   	mov	RTC0ADR, #(0x10 OR RTC0CN)	; enable SmaRTClock
	mov	RTC0DAT, #(RTC0CN_Local OR RTC0TR OR RTC0AEN OR ALRM)  
	ret

;-----------------------LCD INTERFACE------------------------------------	
write_LCD:
	setb 	P1.2	        		; Data mode
	sjmp 	command_LCD+2		;
	
command_LCD:
	clr 	P1.2	       		; Command mode
	mov 	C, ACC.4
	mov	P1.0, C
	mov	C, ACC.5	
	mov 	P0.7, C
	mov	C, ACC.6	
	mov 	P0.6, C
	mov	C, ACC.7	
	mov 	P0.5, C
	setb 	P1.1		   		; raise E to high
	sjmp	$+2			      ; short delay
	clr 	P1.1		   		; lower E to low

	mov 	C, ACC.0			; this second section is the same
	mov	P1.0, C			; only with a swap command added in
	mov	C, ACC.1	
	mov 	P0.7, C
	mov	C, ACC.2	
	mov 	P0.6, C
	mov	C, ACC.3	
	mov 	P0.5, C
	setb 	P1.1		   		; raise E to high
	sjmp	$+2			     	; short delay
	clr 	P1.1		   		; lower E to low
	acall	Delay5ms
	ret

;------------------------------------------------------------------------
Init_LCD:
	mov	TL0, #0
	mov	TH0, #0
	acall	Delay5ms+6			; set 1 sec delay

	clr 	P1.1			    	; make sure that E=0
	acall	Delay5ms			; make a 15 msec power-up delay
	acall	Delay5ms
	acall	Delay5ms

	mov	P0, #0x9F			; send wake-up command 0x30	
	setb	P1.0
	setb	P1.1				; latch data in
	sjmp	$+2				; short delay
	clr	P1.1
	
	acall	Delay5ms
	mov	A, #0x28			; send 4-bit, 2 line mode command
	acall	command_LCD	
	mov	A, #0x28			; send it again			
	acall	command_LCD
	mov	A, #0x0F			; display on, blink cursor
	acall	command_LCD			
	mov	A, #0x01			; clear display
	acall	command_LCD
	mov	A, #0x06			; entry mode set
	acall	command_LCD
	mov	A, #0x80			; set cursor			
	acall	command_LCD
	ret

;------------------------------------------------------------------------
Refresh_Line2:
     	jb 	Temp, $+5
	ajmp	Display_Date		; Temp bit = 0
  	ajmp 	Display_Temp		; Temp bit = 1

;------------------------------------------------------------------------	
Delay5ms:
	clr	A
	sjmp	$+2
	sjmp	$+2
	sjmp	$+2
	sjmp	$+2
	sjmp	$+2
	sjmp	$+2
	sjmp  $+2
	sjmp  $+2
	djnz	ACC, Delay5ms+1		; 3 cycles
	ret

;=========================I2C interface==================================
Start_I2C:
	setb	STA				; Free Smb interupt and signal start bit
	clr	SI
	jnb 	SI, $				; Wait until SMB Interupt is raised
	clr	STA
	ret

Stop_I2C:
	setb 	STO				; Free Smb interupt and signal stop bit
	clr	SI	
	jb 	MASTER, $			; Wait until SMB Interupt is raised	
	ret

Read_I2C:
	clr 	SI				; Free Smb interupt	
	jnb 	SI, $				; Wait until system is done
	mov 	A, SMB0DAT			; Move the recieved data to ACC
	ret	

Write_I2C:
	clr 	SI				; Free Smb interupt	
	jnb 	SI, $				; Wait until system is done
	ret

;-----------------------------------------------------------------
Display_Temp:
	orl	TCON, #0x40			; enable Timer 1
	orl	SMB0CF, #0x80		; enable SMBUS
	acall	Start_I2C			; send start condition
	mov	SMB0DAT, #0x9A		; sensor address
	acall	Write_I2C
	mov	SMB0DAT, #0x01		; temp CN reg address
	acall	Write_I2C
	mov	SMB0DAT, #0x81		; temp sensor CN register: enable one-shot
	acall	Write_I2C
	acall	Stop_I2C

	mov 	B, #17			; delay 75 ms
	acall	Delay5ms
	djnz	B, $-2

	acall	Start_I2C	
	mov 	SMB0DAT, #0x9A		; copy temp sensor's adress to i2c (+write mode)
	acall Write_I2C
	mov 	SMB0DAT,  #0x00		; temp CN register adress
	acall Write_I2C
	acall	Start_I2C			; restart I2C
	mov 	SMB0DAT, #0x9B		; copy temp sensor's adress to i2c (+write mode)
	acall Write_I2C
	acall Read_I2C			; get byte
	clr	ACK				; end read
	acall Stop_I2C			; ACC = MSB(temp)

	anl	SMB0CF, #0x7F		; disable SMBUS
	anl	TCON, #0xBF			; disable Timer 1

	push	ACC				; displaying TEMP
	mov 	A, #0xC0			; set cursor
	acall command_LCD
	mov	A, #0x20			;Display 2 blank cells
	acall write_LCD
	mov	A, #0x20
	acall write_LCD
	pop	ACC

	mov 	B,  #0x0A			; Load 10 to B
	div 	AB				; Divide A by 10
	push 	B				; save the second digit 
	add	A,  #0x30			; add 48 to MSD	
	acall write_LCD			; write MSD

	pop 	ACC				; restore 2nd digit			
	add 	A, #0x30			; add 48 to LSD
	acall write_LCD			; write LSD

	mov 	A, #0xDF			; write degree symbol
	acall write_LCD

	mov 	A, #0x43			; write "C"
	acall write_LCD

	mov	A, #0x20			;Display 2 blank cells
	acall write_LCD
	mov	A, #0x20
	acall write_LCD
	ret

;-------------------------------------------------------------
Get_Time_Date:
	orl 	TCON, #0x40        	; enable Timer 1
	orl	SMB0CF, #0x80           ; enable SMBUS

	acall Start_I2C
	mov  	SMB0DAT, #0xD0        	; RTC chip address + write
	acall	Write_I2C
	mov  	SMB0DAT, #0x01        	; minutes register (into the register pointer)
	acall	Write_I2C
	acall Start_I2C 			; restart SMB
	mov 	SMB0DAT, #0xD1        	; RTC chip address + read
	acall Write_I2C
	acall Read_I2C  			; read minute bit
	mov  	min, A  			; store minute bit
	setb	ACK				; Ack this
	acall Read_I2C  			; read hour bit
	mov 	hour, A 			; store hour bit
	setb	ACK				; Ack this
	acall Read_I2C  			; read day byte
	acall	Read_I2C
	mov  	day, A  			; store day byte
	setb	ACK				; Ack this
	acall Read_I2C  			; read month byte
	mov 	month, A 			; store month byte
	setb	ACK				; Ack this
	acall Read_I2C  			; read year byte
	mov 	year, A 			; store year byte
	clr	ACK				; Noack this
	acall Stop_I2C

	anl 	SMB0CF, #0x7F           ; disable SMBUS
	anl 	TCON, #0xBF          	; disable Timer 1
	ret

;-------------------------------------------------------------
Display_Time:
	mov 	A, #0x80        		; move cursor to first character first line
	acall command_LCD
	mov 	A, hour
	swap  A         			; swap the hour byte
	anl	A, #0x1          		; mask to only first bit
	add 	A, #0x30 			; add 48D
	acall Write_LCD 			; write 10 hour to lcd
	mov 	A, hour  			; restore hour
	anl 	A, #0x0F 			; mask to only first nybble
	add 	A, #0x30 			; add 48D
	acall Write_LCD 			; write 10 hour to lcd

	mov 	A, #0x3A         		; write ":"
	acall Write_LCD

	mov  	A, min           		; restore minute
	swap 	A               		; swap 10s and 1s nybbles
	anl  	A, #0x0F 			; mask to only first nybble (tens of minutes)
	add  	A, #0x30 			; add 48D
	acall Write_LCD 			; write 10 hour to lcd
	mov 	A, min           		; restore minute
	anl 	A, #0x0F 			; mask to only first nybble (minutes)
	add 	A, #0x30 			; add 48D
	acall Write_LCD 			; write 1 hour to lcd

	mov  	A, #0x20              	; write " "
	acall Write_LCD

	mov  	A, hour  			; load hour into ACC
	jb   	ACC.5, TRPM

	mov  	A, #0x41               	; write "A"
	acall write_LCD
	sjmp 	TRDone
TRPM:                           	; jump if PM
	mov  	A, #0x50              	; write "P"
	acall Write_LCD
TRDone:                 		; jump when done with AM
	mov  	A, #0x4D              	; write "M"
	acall Write_LCD
	ret

;-------------------------------------------------------------
DisplayAlarmTime:
	mov 	A, #0xC0        		; move cursor to first character second line
	acall command_LCD
	mov 	A, alhour
	swap  A         			; swap the hour byte
	anl	A, #0x1          		; mask to only first bit
	add 	A, #0x30 			; add 48D
	acall Write_LCD 			; write 10 hour to lcd
	mov 	A, alhour  			; restore hour
	anl 	A, #0x0F 			; mask to only first nybble
	add 	A, #0x30 			; add 48D
	acall Write_LCD 			; write 10 hour to lcd

	mov 	A, #0x3A         		; write ":"
	acall Write_LCD

	mov  	A, almin           	; restore minute
	swap 	A               		; swap 10s and 1s nybbles
	anl  	A, #0x0F 			; mask to only first nybble (tens of minutes)
	add  	A, #0x30 			; add 48D
	acall Write_LCD 			; write 10 hour to lcd
	mov 	A, almin           	; restore minute
	anl 	A, #0x0F 			; mask to only first nybble (minutes)
	add 	A, #0x30 			; add 48D
	acall Write_LCD 			; write 1 hour to lcd

	mov  	A, #0x20              	; write " "
	acall Write_LCD

	mov  	A, alhour  			; load hour into ACC
	jb   	ACC.5, ATRPM

	mov  	A, #0x41               	; write "A"
	acall write_LCD
	sjmp 	ATRDone
ATRPM:                           	; jump if PM
	mov  	A, #0x50              	; write "P"
	acall Write_LCD
ATRDone:                 		; jump when done with AM
	mov  	A, #0x4D              	; write "M"
	acall Write_LCD
	ret	

;-------------------------------------------------------------
Display_Date:
	mov 	A, #0xC0        		; move cursor to first character second line
	acall command_LCD
	mov 	A, day
	swap  A         			; swap the day byte
	anl	A, #0x3          		; mask to only first 2 bits
	add 	A, #0x30 			; add 48D
	acall Write_LCD 			; write 10s to lcd
	mov 	A, day  			; restore day
	anl 	A, #0x0F 			; mask to only first nybble
	add 	A, #0x30 			; add 48D
	acall Write_LCD 			; write 1s to lcd

	mov 	A, #47         		; write "/"
	acall Write_LCD

	mov 	A, month
	swap  A         			; swap the month byte
	anl	A, #0x1          		; mask to only first 1 bit
	add 	A, #0x30 			; add 48D
	acall Write_LCD 			; write 10s to lcd
	mov 	A, month  			; restore month
	anl 	A, #0x0F 			; mask to only first nybble
	add 	A, #0x30 			; add 48D
	acall Write_LCD 			; write 1s to lcd

	mov 	A, #47         		; write "/"
	acall Write_LCD

	mov 	A, year
	swap  A         			; swap the month byte
	anl	A, #0x0F          	; mask to only first nybble
	add 	A, #0x30 			; add 48D
	acall Write_LCD 			; write 10s to lcd
	mov 	A, year  			; restore month
	anl 	A, #0x0F 			; mask to only first nybble
	add 	A, #0x30 			; add 48D
	acall Write_LCD 			; write 1s to lcd
	ret

;-------------------------------------------------------------
Inc_Hour:
	mov	C, ACC.ampm			; copy AM/PM to F0
	mov	F0, C
	anl	A, #0x1F			; leave only hour digits
	add	A, #0x01
	da	A
	cjne	A, #0x13, $+5
	mov	A, #1

	mov	C, F0
	mov	ACC.ampm, C
	setb	ACC.6
	ret

;-------------------------------------------------------------
Dec_Hour:
	mov	C, ACC.ampm			; copy AM/PM to F0
	mov	F0, C
	anl	A, #0x1F			; leave only hour digits
	add	A, #0x99
	da	A
	cjne	A, #0x00, $+5
	mov	A, #0x12

	mov	C, F0
	mov	ACC.ampm, C
	setb	ACC.6
	ret

;-------------------------------------------------------------
Reset_RTC_Interupts:			; value to send to RTC is in A
	orl 	TCON, #0x40      		; enable Timer 1
	orl 	SMB0CF, #0x80           ; enable SMBUS
	acall Start_I2C
	mov 	SMB0DAT, #0xD0        	; RTC chip address + write
	acall	Write_I2C
	mov 	SMB0DAT, #0x0F        	; register pointer
	acall Write_I2C
	mov 	SMB0DAT, #0x00		; reset flags
	acall	Write_I2C
	acall Stop_I2C
	anl	SMB0CF, #0x7F           ; disable SMBUS
	anl 	TCON, #0xDF         	; disable Timer 1
	ret

;---------------------------------------------------------------
Minute_Interrupt:
	push	ACC
	push	PSW
	mov	A, state
	jz	update1
	add	A, #-7
	jc	update2
update1:					; if state=0 or state >= 7
	acall Get_Time_Date
	sjmp	mdt
update2:					; if state!=0 or state < 7 
	inc	min
mdt:
	acall	Display_Time
	mov	A, state
	jnz	Minute_Exit
	acall Refresh_Line2

Minute_Exit:
	jb	P2.7, rmi
	jnb	Alarm, rmi
	acall	AlarmOn
rmi:  acall Reset_RTC_Interupts
	pop	PSW
	pop	ACC
	ret

;-------------------------------------------------------------
SetCursor:					; puts cursor to position in A
	orl	A, #0x80			; row:column, both start with 0
	jnb	ACC.4, $+5
	add	A, #0x30
	acall command_LCD	
	ret

;---------------------------------------------------------------
FSM:	mov	DPTR, #FSMtab		; main Finite State Machine
	mov	A, state			; implementing the UI
	add	A, ACC
	jmp	@A+DPTR			; table jump to state processing

FSMtab:
	ajmp	state0
	ajmp	state1
	ajmp	state2
	ajmp	state3
	ajmp	state4
	ajmp	state5
	ajmp	state6
	ajmp	state7
	ajmp	state8
	ajmp	state9

;--------------------------------------------------------------------
state0:
	jnb	bStati.alarmB, s0a	; alarm button action
	jnb	AlarmRings, $+6
	acall	AlarmOff
	ret
	acall	DisplayAlarmTime
	mov	A, #0x10
	acall	SetCursor
	mov	state, #7
	ret

s0a:	jnb	bStati.setupB, s0b	; setup button action	
	mov	C, Temp			; save Temp bit in F1
;	mov	F1, C
;	clr	Temp
;	acall	Refresh_Line2
	acall	Display_Date
	mov	A, #0x00
	acall	SetCursor
	mov	state, #1
	ret	

s0b:	jnb	bStati.snoozeB, s0c	; snooze button action
	cpl	Temp
	acall	Refresh_Line2
	ret

s0c:	jnb	bStati.dimB, s0d		; dim button action
	cpl	Backlight
	acall	SetBackLight
s0d:	ret

;--------------------------------------------------------------------
state1: ; time hour
	jnb	bStati.alarmB, s1a	; alarm button action
	acall	Get_Time_Date
	acall	Display_Time
;	mov	C, F1				; restore Temp bit
;	mov	Temp, C
	acall Refresh_Line2
	dec	state
	ret
s1a:	jnb	bStati.setupB, s1b	; setup button action
	mov	A, #0x03
	acall	SetCursor
	inc	state
	ret
s1b:	jnb	bStati.snoozeB, s1c	; snooze button action
	mov	A, hour
	acall	Inc_Hour
	mov	hour, A
	acall Display_Time
	mov	A, #0x00
	acall	SetCursor
	ret
s1c:	jnb	bStati.dimB, s1d		; dim button action
	mov	A,hour
      acall	Dec_hour
	mov	hour, A
	acall Display_Time
	mov	A, #0x00
	acall	SetCursor
s1d:	ret

;--------------------------------------------------------------------
state2: ; time minute
	jnb	bStati.alarmB, s2a	; alarm button action
	mov	A, #0x00
	acall	SetCursor
	dec	state
	ret
s2a:	jnb	bStati.setupB, s2b	; setup button action
	mov	A, #0x06
	acall	SetCursor
	inc	state
	ret
s2b:	jnb	bStati.snoozeB, s2c	; snooze button action
	mov	A, min
	add	A, #0x01
	da	A
	cjne	A, #0x60, $+4
	clr	A
	mov	min, A
	acall Display_Time
	mov	A, #0x03
	acall	SetCursor
	ret
s2c:	jnb	bStati.dimB, s2d		; dim button action
	mov	A, min
      add	A, #0x99
	da	A
	cjne	A, #0x99, $+5
	mov	A, #0x59
	mov	min, A
	acall display_time
	mov	A, #0x03
	acall	SetCursor
s2d:	ret

;--------------------------------------------------------------------
state3: ;time AM/PM
	jnb	bStati.alarmB, s3a	; alarm button action
	mov	A, #0x03
	acall	SetCursor
	dec	state
	ret
s3a:	jnb	bStati.setupB, s3b	; setup button action
	mov	A, #0x10
	acall	SetCursor
	inc	state
	ret
s3b:	jnb	bStati.snoozeB, s3c	; snooze button action
	cpl	hour.ampm
	acall Display_Time
	mov	A, #0x06
	acall	SetCursor
	ret
s3c:	jnb	bStati.dimB, s3d		; dim button action
	cpl	hour.ampm
	acall Display_Time
	mov	A, #0x06
	acall	SetCursor
s3d:	ret

;--------------------------------------------------------------------
state4: ;time date
	jnb	bStati.alarmB, s4a	; alarm button action
	mov	A, #0x06
	acall	SetCursor
	dec	state
	ret
s4a:	jnb	bStati.setupB, s4b	; setup button action
	mov	A, #0x13
	acall	SetCursor
	inc	state
	ret
s4b:	jnb	bStati.snoozeB, s4c	; snooze button action
	mov	A, day
	add	A, #0x01
	da	A
	cjne	A, #0x32, $+5
	mov	A, #0x01	
	mov	day, A
	acall Display_Date
	mov	A, #0x10
	acall	SetCursor
	ret
s4c:	jnb	bStati.dimB, s4d		; dim button action
	mov	A, day
      add	A, #0x99
	da	A
	cjne	A, #0x00, $+5
	mov	A, #0x31
	mov	day, A
	acall Display_Date
	mov	A, #0x10
	acall	SetCursor
s4d:	ret

;--------------------------------------------------------------------
state5: ;time month
	jnb	bStati.alarmB, s5a	; alarm button action
	mov	A, #0x10
	acall	SetCursor
	dec	state
	ret
s5a:	jnb	bStati.setupB, s5b	; setup button action
	mov	A, #0x16
	acall	SetCursor
	inc	state
	ret
s5b:	jnb	bStati.snoozeB, s5c	; snooze button action
	mov	A, month
	add	A, #0x01
	da	A
	cjne	A, #0x13, $+5
	mov	A, #0x01
	mov	month, A
	acall Display_Date
	mov	A, #0x14
	acall	SetCursor
	ret
s5c:	jnb	bStati.dimB, s5d		; dim button action
	mov	A, month
      add	A, #0x99
	da	A
	cjne	A, #0x00, $+5
	mov	A, #0x12
	mov	month, A
	acall Display_Date
	mov	A, #0x14
	acall	SetCursor
s5d:	ret

;--------------------------------------------------------------------
state6: ;time year
	jnb	bStati.alarmB, s6a	; alarm button action
	mov	A, #0x14
	acall	SetCursor
	dec	state
	ret
s6a:	jnb	bStati.setupB, s6b	; setup button action
	acall Set_Time_Date
;	mov	C, F1				; restore Temp bit
;	mov	Temp, C
	acall Refresh_Line2
	mov	state, #0
	ret
s6b:	jnb	bStati.snoozeB, s6c	; snooze button action
	mov	A, year
	add	A, #0x01
	da	A
	mov	year, A
	acall Display_Date
	mov	A, #0x16
	acall	SetCursor
	ret
s6c:	jnb	bStati.dimB, s6d		; dim button action
	mov	A, year
      add	A, #0x99
	da	A
	mov	year, A
	acall Display_Date
	mov	A, #0x16
	acall	SetCursor
s6d:	ret

;--------------------------------------------------------------------
state7: ;alarm hours
	jnb	bStati.alarmB, s7a	; alarm button action
	acall	Refresh_Line2
	clr	Alarm
	mov	state, #0
	ret
s7a:	jnb	bStati.setupB, s7b	; setup button action
	mov	A, #0x14
	acall	SetCursor
	inc	state
	ret
s7b:	jnb	bStati.snoozeB, s7c	; snooze button action
	mov	A, alhour
	call	Inc_Hour
	mov	alhour, A
	acall DisplayAlarmTime
	mov	A, #0x10
	acall	SetCursor
	ret
s7c:	jnb	bStati.dimB, s7d		; dim button action
	mov	A, alhour
      acall	Dec_Hour
	mov	alhour, A
	acall DisplayAlarmTime
	mov	A, #0x10
	acall	SetCursor
s7d:	ret

;--------------------------------------------------------------------
state8: ;alarm minutes
	jnb	bStati.alarmB, s8a	; alarm button action
	mov	A, #0x10
	acall	SetCursor
	dec	state
	ret
s8a:	jnb	bStati.setupB, s8b	; setup button action
	mov	A, #0x16
	acall	SetCursor
	inc	state
	ret
s8b:	jnb	bStati.snoozeB, s8c	; snooze button action
	mov	A, almin
	add	A, #0x01
	da	A
	cjne	A, #0x60, $+4
	clr	A
	mov	almin, A
	acall DisplayAlarmTime
	mov	A, #0x14
	acall	SetCursor
	ret
s8c:	jnb	bStati.dimB, s8d		; dim button action
	mov	A, almin
      add	A, #0x99
	da	A
	cjne	A, #0x99, $+5
	mov	A, #0x59
	mov	almin, A
	acall DisplayAlarmTime
	mov	A, #0x14
	acall	SetCursor
s8d:	ret

;--------------------------------------------------------------------
state9: ;alarm AM/PM
	jnb	bStati.alarmB, s9a	; alarm button action
	mov	A, #0x14
	acall	SetCursor
	dec	state
	ret
s9a:	jnb	bStati.setupB, s9b	; setup button action
	acall	Refresh_Line2
	lcall SetAlarm
	mov	state, #0
	ret
s9b:	jnb	bStati.snoozeB, s9c	; snooze button action
	cpl	alhour.ampm
	acall DisplayAlarmTime
	mov	A, #0x16
	acall	SetCursor
	ret
s9c:	jnb	bStati.dimB, s9d		; dim button action
	cpl	alhour.ampm
	acall DisplayAlarmTime
	mov	A, #0x16
	acall	SetCursor
s9d:	ret

;-------------------------------------------------------------
Set_Time_Date:
	orl 	TCON, #0x40        	; enable Timer 1
	orl	SMB0CF, #0x80           ; enable SMBUS

	lcall Start_I2C
	mov  	SMB0DAT, #0xD0        	; RTC chip address + write
	lcall	Write_I2C
	mov  	SMB0DAT, #0x01        	; Minute register (into the register pointer)
	lcall	Write_I2C
	mov	SMB0DAT, min
	lcall Write_I2C
	mov	SMB0DAT, hour
	lcall Write_I2C
	mov 	SMB0DAT, #0x01
	lcall Write_I2C
	mov	SMB0DAT, day
	lcall Write_I2C
	mov	SMB0DAT, month
	lcall Write_I2C
	mov	SMB0DAT, year
	lcall Write_I2C
	lcall Stop_I2C

	anl 	SMB0CF, #0x7F           ; disable SMBUS
	anl 	TCON, #0xBF          	; disable Timer 1
	ret

;-------------------------------------------------------------
SetAlarm:
	orl 	TCON, #0x40        	; enable Timer 1
	orl	SMB0CF, #0x80           ; enable SMBUS

	lcall Start_I2C
	mov  	SMB0DAT, #0xD0        	; RTC chip address + write
	lcall	Write_I2C
	mov  	SMB0DAT, #0x08        	; Alarm Minute register (into the register pointer)
	lcall	Write_I2C
	mov	SMB0DAT, almin
	lcall Write_I2C
	mov	SMB0DAT, alhour
	lcall Write_I2C
	lcall Stop_I2C

	anl 	SMB0CF, #0x7F           ; disable SMBUS
	anl 	TCON, #0xBF          	; disable Timer 1
	setb	Alarm
	ret	

;-------------------------------------------------------------
SetBackLight:
	mov	C, Power
	anl	C, Backlight
	cpl	C
	mov	P1.4, C
	ret

AlarmOn:					; start alarm ring tone
	mov	PCA0CN, #0x40		; enable PCA0 module
	anl	P1SKIP, #0xDF		; assign PCA0 output at P1.5
	setb	AlarmRings			; set alarm ring status = ON
	mov	TMR2L, #0
	mov	TMR2H, #0
	setb	ET2				; enable Timer2 interrupt
	mov	TMR2CN, #0x04		; enable Timer2
	ret

AlarmOff:					; stop alarm ring tone
	clr	ET2				; disable Timer2 interrupt
	mov	TMR2CN, #0			; disable Timer2
	mov	PCA0CN, #0			; disable PCA0 module
	orl	P1SKIP, #0x20		; output 0 to P1.5
	clr	AlarmRings			; clear alarm ring status
	clr	Alarm
	ret

LPM:						; switch to low-power mode
	setb	P1.6				; turn off LCD

	orl	P0MASK, #0x10		; enable Port match on P0.4
	orl	P0MAT,  #0x10

LPM_loop:
	mov	CLKSEL, #0x04		; switch to low poser 20 MHz oscillator
	mov	A, CLKSEL
	jnb	ACC.7, $-2			; wait for divider setting to be applied
;	anl	FLSCL, #NOT(BYPASS)

	mov	PMU0CF, #0x20		; clear wake-up flags
	mov	PMU0CF, #0x84		; specify wake-up source (SmaRTClock)	
	
	nop
	nop
	nop
	nop
	
	mov	CLKSEL, #0x44		; restore clock source
	mov	A, CLKSEL
	jnb	ACC.7, $-2			; wait for divider setting to be applied
;	anl	FLSCL, #NOT(BYPASS)

	setb	AD0EN				; turn ADC on

	mov  	ADC0MX, #0x0B		; set input P1.3 for the power line	
	mov	A, #4				; 10 usec delay
	djnz	ACC, $
	setb	AD0BUSY			; request conversion
	nop
	nop		
	jb	AD0BUSY, $			; wait for conversion end

	clr	AD0EN				; disable ADC	
	mov	A, ADC0H			; get external voltage value
	subb	A, #85			; power present?
	jc	LPM_loop			; NO - keep waiting
	
	setb	P1.6				; turn on LCD
	setb	Power

	call	Init_LCD
						; need to do: display stuff on LCD

	ret

END