; Clock
;
; Processor pin allocations are as follows:

; RA0 Output driving B input of 4051
; RA1 Output driving C input of 4051
; RA2 Output driving A input of 4051
; RA3 Input switches up and down
; RA4 Input from LDR

; RB0 C segment
; RB1 d segment 
; RB2 e segment 
; RB3 dp segment 
; RB4 g segment 
; RB5 b segment 
; RB6 a segment 
; RB7 f segment 

; CPU configuration
; 	
	list P=16F84
	#include "p16f84.inc"
	__config _XT_OSC & _WDT_OFF & _PWRTE_ON

; Define variables at memory locations

; EEPROM DATA

EEPROM1		equ	H'00'	; non-volatile storage of 12/24hr and display variation 
EEPROM2		equ	H'01'	; storage of time adjust
EEPROM3		equ	H'02'	; storage of time adjust (-) sign

; RAM

DISP1		equ	H'0C'	; working storage for Display1 value 
DISP2		equ	H'0D'	; working storage for Display2 value 
DISP3		equ	H'0E'	; working storage for Display3 value 
DISP4		equ	H'0F'	; working storage for Display4 value
DISP5		equ	H'10'	; working storage for Display5 value
DISP6		equ	H'11'	; working storage for Display6 value
STATUS_TMP 	equ 	H'12'	; temp storage for status during interrupt
W_TMP		equ	H'13'	; temporary storage for w during interrupt
VALUE_1		equ 	H'14'	; delay value storage			
VALUE_2		equ	H'15'	; delay value storage
FLAG_1		equ	H'16'	; bit0, 0.5 second, bit1, 12/24 hr, bit 2, multiplex, 
;				; bit 3, standard/variant display, bit 4, colon, bit 5, AM indicator
;				; bit 6 +/- time adjust, bit 7, time adjust mode 
CNT_8		equ	H'17'	; BCD conversion counter 
TEMP		equ	H'18'	; temporary storage during BCD conversion
TEMP_1		equ	H'19'	; temporary working storage
TEMP_2		equ	H'1A'	; temp storage
TEMP_I		equ	H'1B'	; temporary store during interrupt routine 
TIME_CNT1	equ	H'1C'	; clock seconds 
TIME_CNT2	equ	H'1D'	; clock minutes
TIME_CNT3	equ	H'1E'	; clock hours
BIN_0		equ	H'1F'	; binary value for BCD conversion
BCD_0		equ	H'20'	; BCD ls bits
BCD_1		equ	H'21'	; BCD ms bits
UPDATE1		equ	H'22'	; milliseconds counter
UPDATE2		equ	H'23'	; milliseconds counter
DISP_Pl		equ	H'24'	; display position counter
DIM_CNT		equ	H'25'	; display dimmer counter
ADJUST1		equ	H'26'	; time adjust seconds/30days
ADJUST2		equ	H'27'	; time adjust flag for +/- bit 0
TDADJST1	equ	H'28'	; lsb of value for time adjust 
TDADJST2	equ	H'29'	; msb of value for time adjust
TDADJST3	equ	H'2A'	; lsb of value for time adjust counter 
TDADJST4	equ	H'2B'	; msb of value for time adjust counter
ACCaLO		equ	H'2C'	; divide routine denominator
ACCaHI		equ	H'2D'	; divide routine denominator
ACCbLO		equ	H'2E'	; divide routine numerator
ACCbHI		equ	H'2F'	; divide routine numerator
ACCcLO		equ	H'30'	; divide routine remainder
ACCcHI		equ	H'31'	; divide routine remainder
ACCdLO		equ	H'32'	; divide routine ACCbLO storage
ACCdHI		equ	H'33'	; divide routine ACCbHI storage

; preprogram EEPROM DATA (00-3F from 2100-213F)
	
	ORG     2100		; start at 00

;set 12hr and variant display	

	DE	B'00001000'	; bit 1 12hr/24hr, bit 3 standard/variant display
	DE	D'00'		; time adjust
	DE	D'00'		; time adjust (-) sign

; define reset and interrupt vector start addresses

	org	0	  	; start at address 0000h
	goto	MAIN		; normal service routines from Reset vector
	org     4		; interrupt vector 0004h, start interrupt routine here
	goto	INTRUPT		; go to start of interrupt routine, bypass subroutines

; subroutine to get seven segment display data.  
; RB7-RB0
 
SVNSEG	addwf	PCL,f		; add value of display to program counter
;                 fabgeDdc	; Dp is decimal point
;		       p
	retlw 	B'00010100'	; 7-segment code 0 
	retlw 	B'11011110'	; 7-segment code 1
	retlw 	B'10000101'	; 7-segment code 2
	retlw 	B'10001100'	; 7-segment code 3
	retlw 	B'01001110'	; 7-segment code 4
	retlw 	B'00101100'	; 7-segment code 5
	retlw 	B'00100100'	; 7-segment code 6
	retlw 	B'10011110'	; 7-segment code 7
	retlw 	B'00000100'	; 7-segment code 8
	retlw 	B'00001100'	; 7-segment code 9
	retlw 	B'11111111'	; 7-segment code blank (0A)
	retlw 	B'11101111'	; 7-segment code (-) minus (0B)

; variant code for 7-segment displays require different code for disp 1,3 and disp 2,4
; disp 1 and 3 require the A and B segments crossed, disp 2 and disp 4 the D and E segments

SVNS_AB addwf	PCL,f		; add value of display to program counter
;                 fbagdDec	; Dp is decimal point
;		       p

	retlw 	B'00010100'	; 7-segment code 0 
	retlw 	B'10111110'	; 7-segment code 1
	retlw 	B'10000101'	; 7-segment code 2
	retlw 	B'10001100'	; 7-segment code 3
	retlw 	B'00101110'	; 7-segment code 4
	retlw 	B'01001100'	; 7-segment code 5
	retlw 	B'01000100'	; 7-segment code 6
	retlw 	B'10011110'	; 7-segment code 7
	retlw 	B'00000100'	; 7-segment code 8
	retlw 	B'00001100'	; 7-segment code 9	
	retlw 	B'11111111'	; 7-segment code blank (0A)
	retlw 	B'11101111'	; 7-segment code (-) minus (0B)

SVNS_DE addwf	PCL,f		; add value of display to program counter
;                 fbagdDec	; Dp is decimal point
;		       p

	retlw 	B'00010100'	; 7-segment code 0 
	retlw 	B'11011110'	; 7-segment code 1
	retlw 	B'10000101'	; 7-segment code 2
	retlw 	B'10000110'	; 7-segment code 3
	retlw 	B'01001110'	; 7-segment code 4
	retlw 	B'00100110'	; 7-segment code 5
	retlw 	B'00100100'	; 7-segment code 6
	retlw 	B'10011110'	; 7-segment code 7
	retlw 	B'00000100'	; 7-segment code 8
	retlw 	B'00000110'	; 7-segment code 9
	retlw 	B'11111111'	; 7-segment code blank (0A)
	retlw 	B'11101111'	; 7-segment code (-) minus (0B)

; subroutine for encoding display position driver to 4051 at A, B & C inputs

DISP_P	addwf	PCL,f		; add w to program counter
	retlw 	B'00000110'	; display 1
	retlw	B'00000010'	; display 2
	retlw 	B'00000001'	; display 3
	retlw	B'00000100'	; display 4
	retlw 	B'00000000'	; display 5
	retlw	B'00000101'	; display 6
	
;******************************************************************************************* 

; INTERRUPT
; interrupt from counter used to multiplex display
; this sucessively switches on Disp1-Disp6 in sequence plus 
; blanking for leading zero on display for Disp1
; uses internal timer to initiate display update
; also updates time
; display dimming uses discharge and recharge of capacitor via LDR holding off display
; lighting until capacitor charges 

; start interrupt by saving w and status registers before altered by interrupt routine

INTRUPT	movwf	W_TMP		; w to w_tmp storage
	swapf	STATUS,w	; status to w
	movwf	STATUS_TMP	; status in status_tmp  
	bcf	STATUS,RP0	; select memory bank 0

	btfsc	INTCON,T0IF	; TMRO overflow interrupt flag 
	goto	T_MPX		; TMRO overflow so increase millisecond counter & multiplex display
	goto	RECLAIM		; end of interrupt reclaim w and status

; increment update counter & multiplex display

T_MPX	movf	TMR0,w		; timer value	
				; freq is 4MHz/4/4/(256(-8+2))=1kHz
	nop
	nop			; synchronise counter read with divide by 4 ie add two cycles
	addlw	0x8		; add to timer register and takes 2 cycles to start counting
	movwf	TMR0		; place in timer (the two cycles plus 2-cycle delay = 4cycles)
	
	bcf	INTCON,T0IF	; clear TMRO interrupt flag
	incfsz	UPDATE1,f	; increase counter 1 skip if overflowed
	goto 	CHK_UPD		; goto check update time
	incf	UPDATE2,f	; increase counter 2 when counter 1 overflows

CHK_UPD	movf	UPDATE2,w	; MS Byte of update counter to w
	xorlw	0x01		; 01 compare MS Byte counter with (500 decimal for .5 second update)
	btfss	STATUS,z	; skip if equal
	goto	MPX_DSP		; not, so continue and multiplex display
	movf	UPDATE1,w	; LS Byte of update counter
	xorlw	0xF4		; 000-1F4=500 compare LS byte counter with (500) Using F4 as 
				; we increment update1 after clearing so miss 1 count
	btfss	STATUS,z	; skip if equal
	goto	MPX_DSP		; not 0.5 second yet
	clrf	UPDATE1		; clear timer counter 1
	clrf	UPDATE2		; and timer 2
	incf	UPDATE1,f	; increase by 1 so if decremented in adjust routine will
				; not go to FF
	bsf	FLAG_1,0	; set bit 0 for 0.5 second flag

; Adjustment of time. Alters update of 0.5s counter to adjust for seconds/60days
; value correction. So skips or adds extra count in update every TDADJST1 & 1 count period.	
; Increment TDADJST3, TDADJST4 adjust values compare with
; TDADJST1, TDADJST2 if equal then clear and INCrement or DECrement
; UPDATE1 by 1 depending on + or minus 
; skip if TDADJST1 & TDADJST2 both 0

	movf	ADJUST1,f	; if ADJUST1 is 0 then bypass	
	btfsc	STATUS,z
 	goto	MPX_DSP		; both zero so bypass adjustment

ADJ_CON	incfsz	TDADJST3,f	; increase counter by 1
	goto	CHK_ADJ		; check adjust time
	incf	TDADJST4,f	; increase when 3 overflows
CHK_ADJ	movf	TDADJST2,w	; ms byte of adjust value	
	xorwf	TDADJST4,w	; compare values
	btfss	STATUS,z	; skip if equal
	goto	MPX_DSP
	movf	TDADJST1,w	; ls byte of adjust value
	xorwf	TDADJST3,w	; compare ls bytes
	btfss	STATUS,z	; skip if equal
	goto	MPX_DSP
	clrf	TDADJST3	; clear counter
	clrf	TDADJST4
	btfss	ADJUST2,0	; if set then a minus, then clock slow so increase update1
				; this will shorten 0.5s period by 1ms every TDADJST count overflow 
	goto	INC_UD1		; increase 0.5s period to slow clock down  
	incf	UPDATE1,f	; 
	goto	MPX_DSP
INC_UD1 decf	UPDATE1,f

; display update 

MPX_DSP	bsf	FLAG_1,2	; set multiplex flag
	movlw	0xFF		; all 1's
	movwf	PORTB		; all segments off
	incf	DISP_PL,f	; increment display position counter
	movlw	0x06		; max display count + 1
	subwf	DISP_PL,w	; subtract 6 from display position counter
	btfsc	STATUS,c	; if carry is 1 then clear disp_pl
	clrf	DISP_PL		; clear counter back to display 1 position
	
; display dimming. RA4 output low. (discharge capacitor). RA4 open (input), wait for input to go high
	
	btfsc	FLAG_1,7	; adjust flag. Do not dim in adjust mode as no time for routines
	goto	DDCOM
	bcf	PORTA,4		; RA4 low
	nop			; time to discharge capacitor 
	nop
	nop
	nop			
	bsf	PORTA,4		; output high (open drain)
	movlw	D'100'		; 256-100 is preset value
	movwf	DIM_CNT		; dimming counter at preset 
CHARGE	btfsc	PORTA,4		; if high continue (capacitor charged via LDR)
	goto	DDCOM		; drive display commons
	incfsz	DIM_CNT,f	; skip when returns to 0 so 256-100 x 5 cycles = 280us < 1ms interrupt rate
	goto	CHARGE		; wait till capacitor charged and increase counter. Counter
;				; prevents time exceeding the multiplex rate at 1ms. Set min display brightness 

; driving display commons
 
DDCOM	movf	DISP_PL,w	; display position counter
	btfsc	STATUS,z	; if zero
	goto	LEAD_0		; leading zero blanking if 0
	call	DISP_P		; display position code
	movwf	PORTA		; drive 4051 and display 1-6 
	goto	CONT

LEAD_0	call	DISP_P		; display position code
	movwf	PORTA		; display 1 common connected (for AM indicator if required)
	movf	DISP1,f		; check if 0
	btfsc	STATUS,z
	goto	EXTRAS		; bypass loading seven segment data
	
CONT	movlw	DISP1		; display address for display1
	movwf	FSR		; special function register for indirect addressing	
	movf	DISP_PL,w	; display counter
	addwf	FSR,f		; FSR plus display counter
	movf	INDF,w		; w has display data
	movwf	TEMP_I		; store w

; check whether standard or variant display FLAG_1,3. Disp1 - Disp4 need different 
; 7-segment code for variant (disp1,3 need a/b cross and disp2,4 need e/f cross)
	
	btfss	FLAG_1,3	; check for standard/variant display
	goto	STAND_D		; standard display
	movf	DISP_PL,w	; display position
	btfsc	STATUS,z	; if zero then get variant 7-segment 
	goto	DISP1_3		; display 1 variant
	xorlw	0x02		; display 3
	btfsc	STATUS,z	; if z is 0 then not display 3
	goto	DISP1_3		; displays 1 & 3
	btfsc	DISP_PL,2	; if bit 2 set then display 4 or 5 (no variant required)
	goto	STAND_D		; standard display
	movf	TEMP_I,w	; display data
	call	SVNS_DE		; 7-seg code for d/e segment transposed
	goto	CODE_S		; set code to port B

DISP1_3	movf	TEMP_I,w	; display data
	call	SVNS_AB
	goto 	CODE_S		; code set to port
	
STAND_D	movf	TEMP_I,w
	call 	SVNSEG		; get equivalent seven segment value
CODE_S	movwf	PORTB		; code to display segments

; add in colon and AM indicators (decimal points)

EXTRAS	btfsc	FLAG_1,7	; time adjust flag
	goto	RECLAIM		; extras not during time adjust routine
	movf	DISP_PL,w	; display position counter
	btfss	STATUS,z	; check for zero for display 1
	goto	COL_CHK		; check for colon
	btfss	FLAG_1,5	; bit 5 is AM flag		 	
	bcf	PORTB,2		; AM indicator on
	goto	RECLAIM

COL_CHK	movwf	TEMP_I		; temporary store during interrupt
	decfsz	TEMP_I,f	; if zero then a colon display
	goto	COL_CK
	goto	COL_DSP		; colon display (2 & 3)
COL_CK	decfsz	TEMP_I,f	
	goto	RECLAIM
	
COL_DSP btfss	FLAG_1,4	; is colon off or on
	goto	RECLAIM
	bcf	PORTB,2		; colon on 

; end of interrupt reclaim w and status 

RECLAIM	swapf	STATUS_TMP,w	; status temp storage to w
	movwf	STATUS		; w to status register
	swapf	W_TMP,f		; swap upper and lower 4-bits in w_tmp
	swapf   W_TMP,w		; swap bits and into w register
	retfie			; return from interrupt

;********************************************************************************************** 
  	
; RESET		
; Set initial conditions


MAIN	clrf	DISP_PL		; display position counter during multiplex
	movlw 	0x01
	movwf	DISP1		; a 1 for 12:00
	movlw	0x02		; a 2 for 12:00
	movwf	DISP2		; display2
	clrf	DISP3		; minutes and seconds 00
	clrf	DISP4		; display 4
	clrf	DISP5
	clrf	DISP6
	clrf	TDADJST3	; time adjust counter
	clrf	TDADJST4	; time adjust counter
	clrf	TIME_CNT1	; seconds counter
	clrf	TIME_CNT2	; minutes counter
	clrf 	UPDATE1		; milliseconds counter
	clrf	UPDATE2		; milliseconds counter
	incf	UPDATE1,f	; add 1 to update counter
	movlw	0x12		; 12 O'clock AM preset (with AM indicator lit)
	movwf	TIME_CNT3	; hours counter
	

; set up ports	
	
	bsf	STATUS,RP0	; select memory bank 1
	movlw	B'00000000'	; w = 00000000 binary (RB0- RB7 outputs)
	movwf	TRISB		; port B data direction register
	movlw	B'00000001'	; w = 10000000 binary 
	movwf	OPTION_REG	; TMRO prescaler 4 division, PORTB pullups enabled
	movlw   B'00001000'	; w = 01100 binary (RA0,1,2 & 4 outputs, RA3 input)
	movwf   TRISA		; A port data direction register
	bcf	STATUS,RP0	; select memory bank 0
	movlw	B'11111111'	; w is all 1's for RB7-RB0 
	movwf	PORTB		; portB outputs high
	movlw	B'000000'
	movwf	PORTA		; portA RA0,RA1,RA2 outputs low

; read data values
	
	movlw	EEPROM1		; options
	call 	EEREAD
	movwf	FLAG_1		; option and operation flags	
	bcf	FLAG_1,7	; time adjust flag off

	movlw	EEPROM2		; adjust storage
	call 	EEREAD
	movwf	ADJUST1		; sec/60days adjust of time store

; use adjust1 value as denominator in divide routine and place in counters TDADJST1, TDADJST2		
; seconds in 60 days = 5184000. Divide by 500 (0.5s counter) = 10368 (used as numerator)

	movwf	ACCaLO		; ADJUST1 denominator for divide
	call	D_DIV		; divide routine
	movf	ACCbHI,w	; ms result
	movwf	TDADJST2	; ms 
	movf	ACCbLO,w	; ls result
	movwf	TDADJST1	; ls 

	movlw	EEPROM3		; adjust storage
	call 	EEREAD
	movwf	ADJUST2		; bit 0 for
				; + or - adjust of time	flag (01 is -), (00 is +)

; read switches for options, RA1 high (HOUR switch) the 12/24 hr option change
; RA0 high (minutes switch) then variant for display. Both switches = time adjust

	bsf	PORTA,0		; minutes switch
	nop			; give time for port to go high
	btfss	PORTA,3		; if high then min switch closed
	goto	HR_SW

; kits r us change next line
	goto	GLOBE		; change for kits r us to prevent standard display

	btfss	FLAG_1,3	; standard/variant display option	
	goto	STAND
	bcf	FLAG_1,3	; set to standard
DISP_S	bcf	PORTA,0
	bcf	PORTA,1		; select display 5
	bcf	PORTA,2
	movlw	B'00101100'	; 7-segment code S
	movwf	PORTB
	call	DELMO
	movlw	B'11111111'	; off
	movwf	PORTB
	call	DELMO
	call	DELMO
	call 	DELMO
	call	DELMO
	call 	DELMO 
	bsf	PORTA,0 	; select min switch again
	nop
	btfss	PORTA,3		; check if switch open
	goto 	STORE
	goto	DISP_S		; display S for standard
STAND	bsf	FLAG_1,3	; set to variant
DISP_V	bcf	PORTA,0
	bcf	PORTA,1
	bcf	PORTA,2
	movlw	B'01010100'	; 7-segment code V
	movwf	PORTB
	call	DELMO
	movlw	B'11111111'	; off
	movwf	PORTB
	call	DELMO
	call	DELMO
	call 	DELMO
	call	DELMO
	call 	DELMO
	bsf	PORTA,0 	; select min switch again
	nop  
	btfss	PORTA,3		; check if switch open
	goto 	STORE
	goto	DISP_V		; display V for Variant

HR_SW	bcf	PORTA,0		; RA0 low again
	bsf	PORTA,1		; hr switch
	bcf	PORTA,2
	nop
	btfss	PORTA,3		; if high then hr switch closed	
	goto	GLOBE
	btfss	FLAG_1,1	; 12/24 display option	
	goto	SET24
	bcf	FLAG_1,1	; set to 12
DISP_2	bcf	PORTA,1		; display 6
	bsf	PORTA,0
	bsf	PORTA,2
	movlw	B'10000101'	; 7-segment code 2
	movwf	PORTB
	call	DELMO		; display on for delay time
	movlw	B'11111111'	; off
	movwf	PORTB
	bcf	PORTA,2		; select disp5
	bcf	PORTA,0
	movlw	B'11011110'	; 7-segment code for 1
	movwf	PORTB
	call	DELMO
	movlw	B'11111111'	; off
	movwf	PORTB
	bsf	PORTA,1		; select 
	call 	DELMO
	call	DELMO
	call 	DELMO
	call	DELMO  
	btfss	PORTA,3		; check if switch open
	goto 	STORE
	goto	DISP_2		; display 12 for 12hr mode

SET24	bsf	FLAG_1,1	; set to variant
DISP_4	bsf	PORTA,2		; display 6
	bsf	PORTA,0
	bcf	PORTA,1
	movlw	B'01001110'	; 7-segment code 4
	movwf	PORTB
	call	DELMO
	movlw	B'11111111'	; off
	movwf	PORTB
	bcf	PORTA,2		; select disp5
	bcf	PORTA,1
	bcf	PORTA,0
	movlw	B'10000101'	; 7-segment code for 2
	movwf	PORTB
	call	DELMO
	movlw	B'11111111'	; off
	movwf	PORTB
	bsf	PORTA,1		; select 
	call	DELMO
	call 	DELMO
	call	DELMO
	call 	DELMO
	btfss	PORTA,3		; check if switch open
	goto 	STORE
	goto	DISP_4		; display 24 for 24hr mode

STORE	movlw	EEPROM1		; EEPROM address
	movwf	EEADR
	movf	FLAG_1,w	; flag options
	call	EWRITE
	goto	GLOBE

ADJ_T   bsf	FLAG_1,7	; adjust flag set (time adjust mode)
	movlw	0x0A		; 10 will show blank when converted to 7-segment data
	movwf	DISP6
	movwf	DISP5
	movwf	DISP1
	call	ADJ_DSP		; adjust value to display

SW_OP	bcf	PORTA,2		; display 5
	bcf	PORTA,0
	bcf	PORTA,1
	movlw	B'00000110'	; 7-segment code A for Adjust
	movwf	PORTB
	call	DELMO
	movlw	B'11111111'	; off
	movwf	PORTB
	bsf	PORTA,2		; select disp 6
	bcf	PORTA,1
	bsf	PORTA,0
	movlw	B'11000000'	; 7-segment code for d in Adjust
	movwf	PORTB
	call	DELMO
	movlw	B'11111111'	; off
	movwf	PORTB
	bsf	PORTA,1		; select
	bsf	PORTA,0 
	call	DELMO
	call 	DELMO
	call	DELMO
	call 	DELMO
	btfss	PORTA,3		; check if switch open
	goto 	BOTH_OP		; both switches open
	goto	SW_OP		; display Ad for adjust til switch opens
	call	DELMO		; delay

BOTH_OP	movlw	0x0A		; 10 will show blank when converted to 7-segment data
	movwf	DISP6
	movwf	DISP5
	
; allow interrupts
	
GLOBE	bsf	INTCON,GIE	; set global interrupt 
	
; check for update flags	
	
TO_DO	bsf	INTCON,T0IE	; set interrupt
	btfsc	FLAG_1,7	; time adjust flag
	goto	T_ADJ
	btfss	FLAG_1,1	; 12/24hr flag
	goto 	FLG_BY		; AM/PM flag bypass
	bsf	FLAG_1,5	; AM/PM indicator off
FLG_BY	btfss	FLAG_1,0	; 0.5s time flag if set then continue
	goto	SW_CHK		; not 0.5s yet so check for switch pressed

	btfsc	FLAG_1,4	; is colon on or off
	goto	COL_CR		; clear colon
	bsf	FLAG_1,4	; set if clear and update clock time
	bcf	FLAG_1,0	; clear 0.5s flag
; seconds counter	
	incf	TIME_CNT1,f	; increment seconds on clock
	movf	TIME_CNT1,w	; look at time_cnt1
	andlw	0x0F		; look at LS 4-bits
	xorlw	0x0A		; TIME_CNT-10 
	btfss	STATUS,z	; if z is 0 then value <10
	goto	TRANS
	movlw	0x06		; add 6 if LS 4-bits are at count of A (A+6=10)
	addwf	TIME_CNT1,f	
	movlw	0x60		; 60 seconds
	subwf	TIME_CNT1,w	; take 60 from seconds counter
	btfss	STATUS,c	; if c is 0 then counter is <60
	goto	TRANS		; time incremented so check switches
	clrf	TIME_CNT1	; seconds to 00
; minutes counter	
	incf	TIME_CNT2,f	; increment minutes	
	movf	TIME_CNT2,w	; look at time_cnt2
	andlw	0x0F		; look at LS 4-bits
	xorlw	0x0A		; 10
	btfss	STATUS,z	; if z is 0 then value <10
	goto	TRANS
	movlw	0x06		; add 6 if LS 4-bits are at count of A (A+6=10)
	addwf	TIME_CNT2,f	
	movlw	0x60		; 60 minutes
	subwf	TIME_CNT2,w	; take 60 from minutes counter
	btfss	STATUS,c	; if c is 0 then counter is <60
	goto	TRANS		; time incremented so check switches
	clrf	TIME_CNT2	; minutes to 00
; hours counter	
	incf	TIME_CNT3,f	; increment hours
	movf	TIME_CNT3,w	; look at time_cnt3
	andlw	0x0F		; look at LS 4-bits
	xorlw	0x0A		; 10
	btfss	STATUS,z	; if z is 0 then value <10
	goto	FLG_12
	movlw	0x06		; add 6 if LS 4-bits are at count of A (A+6=10)
	addwf	TIME_CNT3,f	

; if 12:00 and in 12hr mode and AM indicator on then turn off. If off then turn on.	
	
FLG_12	btfsc	FLAG_1,1	; 12/24hr flag
	goto 	HR24
	movlw	0x12		; 12 
	xorwf	TIME_CNT3,w	; take 12 from hour counter
	btfss	STATUS,z	; if z is 1 then counter is 12
	goto	CHK12		; check for count from 12 to 1
	btfss	FLAG_1,5	; is AM indicator on
	goto 	SET_AM		; set the AM indicator
	bcf	FLAG_1,5	; clear AM indicator
	goto	CHK12
SET_AM	bsf	FLAG_1,5	; set AM indicator

CHK12	movlw	0x13		; 13 
	subwf	TIME_CNT3,w	; take 13 from hour counter
	btfss	STATUS,c	; if c is zero then counter is <13
	goto	TRANS		; not 12 + yet
	movlw	0x01		; 1:00:00
	movwf	TIME_CNT3	; hours set to 1
	goto	TRANS
HR24	bsf	FLAG_1,5	; AM/PM indicator off
	movlw	0x24		; 24
	subwf	TIME_CNT3,w	; take 24 from hour counter
	btfss	STATUS,c	; if c is zero then counter is <24
	goto	TRANS		; not 24 + yet
	movlw	0x00		; 0:00:00
	movwf	TIME_CNT3	; hours set to 0
	goto	TRANS
COL_CR	bcf	FLAG_1,4	; colon off	
	bcf	FLAG_1,0	; 0.5s flag clear

; transfer TIME_CNT1,2&3 to DISP1-6 

TRANS	call	TM_DSP		; time to display
	goto	SW_CHK

T_ADJ	nop
	
; check for closed switch

SW_CHK	bsf	INTCON,T0IE	; allow interrupts
	bcf	FLAG_1,2	; clear multiplex update flag
FLG	btfss	FLAG_1,2	; is multiplex flag set
	goto	FLG		; not set so wait	
	
	bcf	INTCON,T0IE	; clear interrupt
	btfss	PORTA,3		; check if switch pressed
	goto	TO_DO		; switch not pressed 
	btfsc	PORTA,0		; is it the minutes switch	
	goto	MINUT
	btfss	PORTA,1		; is it the Hours switch
	goto	TO_DO		; no switch pressed
	
; increase hours

; check if both switches are pressed first
	
	btfsc	FLAG_1,7	; time adjust flag
	goto	TADJ_UP		; time adjust up
	movlw	0xFF		; all high
	movwf	PORTB		; displays off
	clrf	PORTA		; all 0	
	bsf	PORTA,0		; check minutes switch
	nop
	btfss	PORTA,3		; is switch closed (bit 3 high)
	goto	HR_UP
	bcf	PORTA,0
	bsf	PORTA,1		; check hour switch
	nop
	btfsc	PORTA,3
	goto	ADJ_T		; time adjust routine

HR_UP	bsf	INTCON,T0IE	; set interrupt

	incf	TIME_CNT3,f	; increment hours
	movf	TIME_CNT3,w	; look at time_cnt3
	andlw	0x0F		; look at LS 4-bits
	xorlw	0x0A		; 10
	btfss	STATUS,z	; if z is 0 then value <10
	goto	FLAG_12
	movlw	0x06		; add 6 if LS 4-bits are at count of A (A+6=10)
	addwf	TIME_CNT3,f	

; if 12:00 and in 12hr mode and AM indicator on then turn off. If off then turn on.	
	
FLAG_12	btfsc	FLAG_1,1	; 12/24hr flag
	goto 	M24HR
	movlw	0x12		; 12 
	xorwf	TIME_CNT3,w	; take 12 from hour counter
	btfss	STATUS,z	; if z is zero then counter is not 12
	goto	M12CHK		; not 12 next check for count from 12 to 1
	btfss	FLAG_1,5	; is AM indicator on
	goto 	MSET_AM		; Manually set the AM indicator
	bcf	FLAG_1,5	; clear AM indicator
	goto	M12CHK
MSET_AM	bsf	FLAG_1,5	; set AM indicator

M12CHK	movlw	0x13		; 13 
	subwf	TIME_CNT3,w	; take 13 from hour counter
	btfss	STATUS,c	; if c is zero then counter is <13
	goto	MTRANS		; not 12 + yet
	movlw	0x01		; 1:00:00
	movwf	TIME_CNT3	; hours set to 1
	goto	MTRANS
M24HR	movlw	0x24		; 24
	subwf	TIME_CNT3,w	; take 24 from hour counter
	btfss	STATUS,c	; if c is zero then counter is <25
	goto	MTRANS		; not 24 + yet
	movlw	0x00		; 0:00:00
	movwf	TIME_CNT3	; hours set to 0
	goto	MTRANS

; check if both switches are pressed first

MINUT	btfsc	FLAG_1,7	; time adjust flag
	goto	TADJ_DN		; time adjust down
	movlw	0xFF		; all high
	movwf	PORTB		; displays off
	clrf	PORTA		; all 0	
	bsf	PORTA,0		; check minutes switch
	nop
	btfss	PORTA,3		; is switch closed (bit 3 high)
	goto	MIN_UP
	bcf	PORTA,0
	bsf	PORTA,1		; check hour switch
	nop
	btfsc	PORTA,3
	goto	ADJ_T		; time adjust routine

MIN_UP	bsf	INTCON,T0IE	; set interrupt
	bcf	FLAG_1,0	; clear 0.5s flag	
	clrf 	UPDATE1		; milliseconds counter
	clrf	UPDATE2		; milliseconds counter
	bsf	FLAG_1,4	; set colon so seconds update correctly at press of switch
	clrf	TIME_CNT1	; seconds to 00
	incf	TIME_CNT2,f	; increment minutes
	movf	TIME_CNT2,w	; look at time_cnt2
	andlw	0x0F		; look at LS 4-bits
	xorlw	0x0A		; 10
	btfss	STATUS,z	; if z is 0 then value <10
	goto	MTRANS
	movlw	0x06		; add 6 if LS 4-bits are at count of A (A+6=10)
	addwf	TIME_CNT2,f		
	movlw	0x60		; 60 minutes
	subwf	TIME_CNT2,w	; take 60 from minutes counter
	btfss	STATUS,c	; if c is 0 then counter is <60
	goto	MTRANS		; time incremented so check switches
	clrf	TIME_CNT2	; back to 00 after 59

; transfer TIME_CNT1,2&3 to DISP1-6 

MTRANS	call	TM_DSP		; time to display
	
WAIT	call	DELTME		; delay
	goto	GLOBE		; continue
	
; Time adjust up and down. Increase or decrease adjust1 store in EEPROM2. 
; If up through 0 change sign to plus (adjust2) if down through 0 change sign to (-) and
; store in EEPROM3.
; update TDADJST1 and TDADJST2 via divide with ADJUST1 as pointer.

TADJ_UP	bsf	INTCON,T0IE	; set interrupt
	
	call	DELTME
	bcf	INTCON,T0IE	; clear interrupt
	movlw	0xFF		; all high
	movwf	PORTB		; displays off
	clrf	PORTA		; all 0	
	bsf	PORTA,1		; check hrs switch
	bcf	PORTA,0
	nop
	btfss	PORTA,3		; is switch closed (bit 3 high)
	goto	GLOBE
	bsf	PORTA,0
	bcf	PORTA,1		; check min switch
	nop
	btfss	PORTA,3
	goto	MIN_US
	bcf	FLAG_1,7	; stop time adjust routine
	bsf	INTCON,T0IE	; set interrupt
	call	TM_DSP		; update time display
	call	DELTME
	call	DELTME
	call	DELTME
	call	DELTME
	call	DELTME
	goto	GLOBE	

MIN_US	bsf	INTCON,T0IE	; set interrupt
	btfsc	ADJUST2,0	; check if - or plus
	goto 	UP_MNUS		; minus so count down to zero  
	incf	ADJUST1,f	; increase adjust value
SHOW	call	ADJ_DSP		; adjust values to display

; store adjust1 and adjust 2 in EEPROM
; update TDADJST1 and TDADJST2

; use adjust1 value as denominator in divide routine and place in counters TDADJST1, TDADJST2		
; seconds in 60 days = 5184000. Divide by 500 (0.5s counter) = 10368 (used as numerator)

	movf	ADJUST1,w
	movwf	ACCaLO		; ADJUST1 denominator for divide
	call	D_DIV		; divide routine
	movf	ACCbHI,w	; ms result
	movwf	TDADJST2	; ms 
	movf	ACCbLO,w	; ls result
	movwf	TDADJST1	; ls 
	
	clrf	TDADJST3
	clrf	TDADJST4

	movlw	EEPROM2		; storage of ADJUST1
	movwf	EEADR
	movf	ADJUST1,w	; adjust value
	call 	EWRITE		; store in EEPROM2

	movlw	EEPROM3		; adjust 2 storage
	movwf	EEADR
	movf	ADJUST2,w	; adjust value
	call 	EWRITE		; store in EEPROM3
	call	DELTME
	goto	GLOBE		; return

UP_MNUS	decfsz	ADJUST1,f	; decrease adjust value
	goto	SHOW
	bcf	ADJUST2,0	; clear (-) sign
	goto	SHOW	

TADJ_DN	bsf	INTCON,T0IE	; set interrupt
	call	DELTME
	
	bcf	INTCON,T0IE	; clear interrupt
	movlw	0xFF		; all high
	movwf	PORTB		; displays off
	clrf	PORTA		; all 0		
	bsf	PORTA,0		; check minutes switch
	bcf	PORTA,1
	nop
	btfss	PORTA,3		; is switch closed (bit 3 high)
	goto	GLOBE
	bcf	PORTA,0
	bsf	PORTA,1		; check hour switch
	nop
	btfss	PORTA,3
	goto	MIN_DN
	bcf	FLAG_1,7	; stop time adjust routine
	bsf	INTCON,T0IE	; set interrupt
	call	TM_DSP		; update time display
	call	DELTME
	call	DELTME
	call	DELTME
	call	DELTME
	call	DELTME
	goto	GLOBE

MIN_DN	bsf	INTCON,T0IE	; set interrupt
	btfsc	ADJUST2,0	; check for -
	goto	DN_MNUS		; minus so count up
	decf	ADJUST1,f	; decrease adjust value
	movlw	0xFF		; check if decreased from 0-255
	xorwf	ADJUST1,w	; compare with 255
	btfss	STATUS,z	; if zero set (-) sign
	goto	SHOW
	incf	ADJUST1,f	; increase again for 0 rather than 255
	incf	ADJUST1,f	; and again for a 1
	bsf	ADJUST2,0	; set (-) sign
	goto 	SHOW
DN_MNUS	incfsz	ADJUST1,f
	goto 	SHOW
	bcf	ADJUST2,0
	goto	SHOW

;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

; SUBROUTINES

; delay period for multiplex rate

DELMO	movlw 	D'255'		; delay period
	movwf	TEMP_2
SMALR	decfsz	TEMP_2,f	; reduce temp_2
	goto	SMALR		; temp_2 smaller by 1
	return			; end delay

; delay time for time set count up rate

DELTME	movlw	D'255'		; first loop delay
	movwf	VALUE_2
LOOP1	movlw	D'255'		; set delay period 
	movwf	VALUE_1		; VALUE_1 = w
	bcf	FLAG_1,2	; clear multiplex update flag
MUL_TIP	btfss	FLAG_1,2	; check if multiplex update occured
	goto	MUL_TIP		; wait for new multiplex update for display
	decfsz	VALUE_2,f	; decrease VALUE_2, skip if zero
	goto	LOOP1		
	return

; place adjust value in displays

ADJ_DSP	movlw	0x0A		; 10 will show blank when converted to 7-segment data
	movwf	DISP6
	movwf	DISP5
	movf	ADJUST1,w	; sec/60days value 
	movwf	BIN_0
	call	BCD		; BCD conversion
	movf	BCD_1,w		; ls bits
	andlw	0x0F		; look at ls 4-bits
	movwf	DISP4		;
	swapf	BCD_1,w		; ms 4-bits swapped with ls 4-bits
	andlw	0x0F		; look at ls bits now ms bits
	movwf	DISP3		; display 3 & 4 show adjust1 value (s/60 days)
	
	movf	BCD_0,w		; ms digit
	andlw	0x0F		; look at ls 4-bits
	btfsc	STATUS,z	; if a zero then set to a blank display (0A=blank for 7-segment) 
	goto	BLNK
	movwf	DISP2	
	movlw	0x0A
	movwf	DISP1		; off - sign
PLS_MIN	btfss	ADJUST2,0	; bit 0 is +/- flag
	return			; 
	incf	DISP1,f		; 0A +1 = 0B (11) sets a (-) in seven segment conversion
				; show - sign when bit 0 in ADJUST2 is set
	return

BLNK	movlw	0x0A
	movwf	DISP2
	movwf	DISP1
	movf	BCD_1,w		; check ls bits again
	sublw	0x09		; take w from 9 if 0 or plus then blank DISP3
	movlw	0x0A		; value to blank segments in 7-segment display
	btfsc	STATUS,c	; if c is zero then negative do not clear disp3
	movwf	DISP3		; display 3 blanked as value <10
	goto	PLS_MIN		; check plus or minus

; transfer time to displays

TM_DSP	movf	TIME_CNT1,w	; seconds counter 
	andlw	0x0F		; get lS bits
	movwf	DISP6		; seconds LS
	swapf	TIME_CNT1,w	; swap LS and MS bits
	andlw	0x0F		; get MS bits
	movwf	DISP5		; seconds MS
	movf	TIME_CNT2,w	; minutes counter 
	andlw	0x0F		; get lS bits
	movwf	DISP4		; minutes LS
	swapf	TIME_CNT2,w	; swap LS and MS bits
	andlw	0x0F		; get MS bits
	movwf	DISP3		; minutes MS
	movf	TIME_CNT3,w	; hours counter 
	andlw	0x0F		; get lS bits
	movwf	DISP2		; hours LS
	swapf	TIME_CNT3,w	; swap LS and MS bits
	andlw	0x0F		; get MS bits
	movwf	DISP1		; hours MS
	return

; subroutine to read EEPROM memory

EEREAD	movwf 	EEADR		; indirect special function register
	bsf 	STATUS,RP0	; select memory bank 1
	bsf	EECON1,RD	; read EEPROM
RD_RD	nop
	btfsc	EECON1,RD	; skip if RD low (read complete)
	goto 	RD_RD		; wait for low RD (read RD)	
	bcf	STATUS,RP0	; select bank 0
	movf	EEDATA,w	; EEPROM value in w
	return

; subroutine to write to EEPROM

EWRITE	bcf	FLAG_1,6	; EEPROM write repeat flag
	movwf	TEMP_2		; store w in temporary storage
EWRIT	movf	TEMP_2,w	; place value in w (used for read data sequence) 
	movwf	EEDATA		; data register
	bcf	INTCON,GIE	; disable interrupts
	bsf	STATUS,RP0	; select bank 1
	bsf	EECON1,WREN	; enable write
	movlw	0x55		; place 55H in w for write sequence
	movwf 	EECON2 		; write 55H to EECON2
	movlw 	0xAA		; AAH to w
	movwf	EECON2		; write AA to EECON2
	bsf	EECON1,WR	; set WR bit and begin write sequence
	bsf	INTCON,GIE	; enable interrupts
	bcf	EECON1,WREN	; clear WREN bit
WRITE	btfsc	EECON1,WR	; skip if write complete WR=0 when write complete
	goto 	WRITE		; not written yet
	bcf	EECON1,EEIF	; clear write interrupt flag 
	
; read EEPROM DATA and check if written correctly
	
	bsf 	STATUS,RP0	; select memory bank 1
	bsf	EECON1,RD	; read EEPROM
RD_AGN	nop
	btfsc	EECON1,RD	; skip if RD low (read complete)
	goto 	RD_AGN		; wait for low RD	
	bcf	STATUS,RP0	; select bank 0
	movf	EEDATA,w	; EEPROM value in w
	subwf	EEDATA,w	; compare read value with value stored
	btfsc	STATUS,z	; skip if not the same
	return			; value correctly written 
	btfsc	FLAG_1,6	; write repeat bit, skip if clear and rewrite
	return
	bsf	FLAG_1,6	; set repeat flag rewrite once only 
	goto 	EWRIT		; rewrite as not written correctly

; Subroutine to convert from 8-bit binary to 2-digit BCD (packed)
; Binary value is in BIN_0  
; Result in BCD is in BCD_0 & BCD_1.  
; BCD_0 is MSB, BCD_1 is LSB

BCD	bcf	STATUS,c	; clear carry bit
	movlw	D'8'
	movwf	CNT_8		; 8 in count
	clrf	BCD_0
	clrf	BCD_1		; set BCD registers to 0 
LOOPBCD	rlf	BIN_0,f		; shift left binary registers
	rlf	BCD_1,f		; MSB shift left

	rlf	BCD_0,f		; LSB shift left BCD registers
	decfsz	CNT_8,f		; reduce count value return when 0
	goto	DECADJ		; continue decimal adjust
	return			; completed decimal to BCD operation

; subroutine decimal adjust

DECADJ	movlw	BCD_1		; BCD LSB address
	movwf	FSR		; pointer for BCD1
	call	ADJBCD		; subroutine to adjust BCD
	movlw	BCD_0		; BCD MS address
	movwf	FSR		; pointer for BCD0
	call	ADJBCD
	goto	LOOPBCD

; subroutine adjust BCD

ADJBCD	movlw	0x03		; w has 03 
	addwf	INDF,w		; add 03 to BCDx register (x is 0-1)
	movwf	TEMP		; store w
	btfsc	TEMP,3		; test if >7
	movwf	INDF		; save as LS digit
	movlw	0x30		; 3 for MSbyte
	addwf	INDF,w		; add 30 to BCDx register
	movwf	TEMP		; store w in temp
	btfsc	TEMP,7		; test if >7
	movwf	INDF		; save as MS digit
	return			; end subroutine


; divide routine denominator in ACCaHI & ACCaLO. Numerator in ACCbHI & ACCbLO. 
; Result in ACCbHI & ACCbLO. Remainder in ACCcHI & ACCcLO.

D_DIV	movlw	D'16'		; 16 shifts
	movwf	TEMP
	movlw	0x28		; 0x28 place 10368 (H2880) in numerator (1s/60days value)	
	movwf	ACCdHI
	movlw	0x80		; 0x80
	movwf	ACCdLO
	clrf	ACCbLO
	clrf	ACCbHI
	clrf	ACCaHI		; clear denominator msb
	clrf	ACCcHI		; remainder ms byte
	clrf	ACCcLO		; remainder ls byte
DLOOP	bcf	STATUS,c	; clear carry
	rlf	ACCdLO,f	; ACCbLO store
	rlf	ACCdHI,f	; ACCbHI store
	rlf	ACCcLO,f	; numerator
	rlf	ACCcHI,f	
	movf	ACCaHI,w	; denominator HI
	subwf	ACCcHI,w	; is denom > remainder
	btfss	STATUS,z
	goto	NOCHK
	movf	ACCaLO,w	; denominator
	subwf	ACCcLO,w	; remainder (checking LSB)
NOCHK	btfss	STATUS,c
	goto	NOGO
	movf	ACCaLO,w	; sub numerator from denominator
	subwf	ACCcLO,f
	btfss	STATUS,c
	decf	ACCcHI,f
	movf	ACCaHI,w
	subwf	ACCcHI,f
	bsf	STATUS,c	; for a 1 into result
NOGO	rlf	ACCbLO,f
	rlf	ACCbHI,f
	decfsz	TEMP,f
	goto	DLOOP
	return


	
	end
