	list      p=16f627a
	#include  p16f627a.inc

	; Set config bits 
	__CONFIG _INTRC_OSC_NOCLKOUT & _PWRTE_OFF & _WDT_OFF & _CP_OFF       

; Compute a value for the timer.
; Goal is an interrupt per millisecond, so we need to divide 
; 4Mhz by 4000 to get 125Hz.  Set the prescaler for 4*(2^6)=256,
; then  set the clock for (5^3)=125.
; Hmm.  It seems OSC1 is already divided by 4 to get CLKOUT, 
; so just use (2^6)=64 for the prescaler.
timeval	EQU	-d'125'

; Declare the time of day variables
	cblock	0x20
	row
	ticks
	seconds
	minutes
	hours
	days
	months
	mode
	bouncing
	savew
	saves
	endc
	
	org	0x0000  ;Reset vector
	goto	main		; Jump to main code defined in Example.asm
	nop			; Pad out so interrupt
	nop			;  service routine gets
	nop			;    put at address 0x0004.
	goto	service 	; Points to interrupt service routine
	org       0x0020	; Begin program
main			; Main code entry -- once only initialization
;OPTION
; Clear RBPU
; Clear TOCS
; Clear PSA
; Set prescaler = 111 (divide by 256)
	clrwdt
	bcf	STATUS, RP1 ; Select BANK0 or BANK1
	bsf	STATUS, RP0 ; Select BANK1
	movlw	(1<<PS2) | (1<<PS1) | (1<<PS0) ; TODO BUGBUG
	movlw	(1<<PS2) | (1<<PS0)
	errorlevel -302
	movwf	OPTION_REG

;INTCON
; Set GIE
; Set T0IE
	bcf	STATUS, RP0 ; Select BANK0
	movlw	(1<<GIE) | (1<<T0IE)
	movwf	INTCON

; Clear PIE1
	bsf	STATUS, RP0 ; Select BANK1
	clrf	PIE1

;PCON
; Set OSCF (4Mhz) (default)
	bsf	STATUS, RP0 ; Select BANK1
	movlw	(1<<OSCF)
	movwf	PCON

;Initialize PORTA as a 4-bit output register
; NOTE: RA5 is input only (watch stem?)
	bcf	STATUS, RP0 ; Select BANK0
	clrf	PORTA
	movlw	0x07	; Disable comparator
	movwf	CMCON
	bsf	STATUS, RP0 ; Select BANK1
	movlw	0x20	; Set PORTA low bits for output
	movwf	TRISA

; Initialize PORTB as a 8-bit output register
	bcf	STATUS, RP0 ; Select BANK0
	clrf	PORTB
	bsf	STATUS, RP0 ; Select BANK1
	clrf	TRISB	; Set PORTB all bits for output

; Initialize the timer
	bcf	STATUS, RP0 ; Select BANK0
	movlw	timeval	; Initialize the timer
	movwf	TMR0

; Initialize the time.
; The time is stored in EEPROM, to reduce setup time
; after battery changes.
	clrf	row
	clrf	ticks
	clrf	seconds
	bsf	STATUS, RP0 ; Select BANK1
	clrf	EEADR
	bsf	EECON1, RD
	movfw	EEDATA
	bcf	STATUS, RP0 ; Select BANK0
	movwf	minutes
	bsf	STATUS, RP0 ; Select BANK1
 	incf	EEADR, F
	bsf	EECON1, RD
	movfw	EEDATA
	bcf	STATUS, RP0 ; Select BANK0
	movwf	hours
	bsf	STATUS, RP0 ; Select BANK1
	incf	EEADR, F
	bsf	EECON1, RD
	movfw	EEDATA
	bcf	STATUS, RP0 ; Select BANK0
	movwf	days
	bsf	STATUS, RP0 ; Select BANK1
	incf	EEADR, F
	bsf	EECON1, RD
	movfw	EEDATA
	bcf	STATUS, RP0 ; Select BANK0
	movwf	months

; Start in Mode 0 -- regular timekeeping.
; The other modes are used to set the clock:
; Mode 1 -- advance months
; Mode 2 -- advance days
; Mode 3 -- advance hours
; Mode 4 -- advance minutes
	clrf	mode

	errorlevel +302
; The main loop implements 'stem' handling.
loop
	; Must be BANK0!
	btfss	PORTA, 5
	goto	loop
; Someone pressed the button!
; Set the debounce flag, advance to the next mode, and wait
; for the button to be released.
	bsf	bouncing, 1

	movfw	mode	; Get current mode
	addlw	-0x4	; Time to wrap to mode 0?
	btfsc	STATUS, Z
	movlw	-0x5	; Yes, set up to get zero result
	addlw	0x5	; Otherwise, restore mode, add one
	movwf	mode	; Store new mode

; Wait for the next interrupt to clear the "bouncing" flag.
debounce1
	btfsc	bouncing, 1
	goto	debounce1
; Wait further for the user to release the button.
waituser
	btfsc	PORTA, 5
	goto	waituser
; Now wait for another debounce.
debounce2
	bsf	bouncing, 1
	btfsc	bouncing, 1
	goto	debounce2
; Kill time until next mode change
	goto	loop
;
; Come here 125 times per second.
;
service 		; Interrupt routine, called to update the display
; First, some housekeeping
	movwf	savew	; Save W
	movfw	STATUS	; Save STATUS
	movwf	saves
	movlw	timeval	; Reset the timer
	movwf	TMR0
	bcf	INTCON, T0IF ; Clear the interrupt flag

; Clear the flag to indicate a debounce interval has ended.
	clrf	bouncing

; Now, increment the time
	bcf	STATUS, RP0 ; Select BANK0
	incf	ticks, F
	movfw	ticks
	sublw	d'125'
	btfss	STATUS, Z
	goto	display
	movwf	ticks	; Wrap Tick Counter

; Dispatch here for modal stem stuff.
	movfw	mode	; Which mode are we in?
	addwf	PCL, F	; Branch based on the digit
	goto	mode0
	goto	mode1
	goto	mode2
	goto	mode3
	goto	mode4

; Mode 0 -- keep accurate time.
mode0
	incf	seconds, F
	movfw	seconds
	sublw	d'60'
	btfss	STATUS, Z
	goto	display
	movwf	seconds	; Wrap Seconds Counter
	incf	minutes, F
	movfw	minutes
	sublw	d'60'
	btfss	STATUS, Z
	goto	savtime
	movwf	minutes	; Wrap Minutes Counter
	incf	hours, F
	movfw	hours
	sublw	d'24'
	btfss	STATUS, Z
	goto	savtime
	movwf	hours	; Wrap Hours Counter
	incf	days, F
	movfw	days
	sublw	d'32'
	btfss	STATUS, Z
	goto	savtime
	movlw	1
	movwf	days	; Wrap Days Counter
	incf	months, F
	movfw	months
	sublw	d'13'
	btfss	STATUS, Z
	goto	savtime
	movlw	1
	movwf	months	; Wrap Months Counter

;
; Update the time in EEPROM
savtime
	errorlevel -302
	bsf	STATUS, RP0 ; Select BANK1
	bsf	EECON1, WREN
	clrf	EEADR
	bcf	STATUS, RP0 ; Select BANK0
	movfw	minutes
	bsf	STATUS, RP0 ; Select BANK1
	movwf	EEDATA
	movlw	0x55
	movwf	EECON2
	movlw	0xAA
	movwf	EECON2
	bsf	EECON1, WR
	btfsc	EECON1, WR
	goto	$-2
	incf	EEADR, F
	bcf	STATUS, RP0 ; Select BANK0
	movfw	hours
	bsf	STATUS, RP0 ; Select BANK1
	movwf	EEDATA
	movlw	0x55
	movwf	EECON2
	movlw	0xAA
	movwf	EECON2
	bsf	EECON1, WR
	btfsc	EECON1, WR
	goto	$-2
	incf	EEADR, F
	bcf	STATUS, RP0 ; Select BANK0
	movfw	days
	bsf	STATUS, RP0 ; Select BANK1
	movwf	EEDATA
	movlw	0x55
	movwf	EECON2
	movlw	0xAA
	movwf	EECON2
	bsf	EECON1, WR
	btfsc	EECON1, WR
	goto	$-2
	incf	EEADR, F
	bcf	STATUS, RP0 ; Select BANK0
	movfw	months
	bsf	STATUS, RP0 ; Select BANK1
	movwf	EEDATA
	movlw	0x55
	movwf	EECON2
	movlw	0xAA
	movwf	EECON2
	bsf	EECON1, WR
	btfsc	EECON1, WR
	goto	$-2
	bcf	STATUS, RP0 ; Select BANK0
	goto	display
;
; Mode 4 -- just increment minutes
mode4
	incf	minutes, F
	movfw	minutes
	sublw	d'60'
	btfss	STATUS, Z
	goto	display
	movwf	minutes	; Wrap Minutes Counter
	goto	display
;
; Mode 3 -- just increment hours
mode3
	incf	hours, F
	movfw	hours
	sublw	d'24'
	btfss	STATUS, Z
	goto	display
	movwf	hours	; Wrap Hours Counter
	goto	display
;
; Mode 2 -- just increment days
mode2
	incf	days, F
	movfw	days
	sublw	d'32'
	btfss	STATUS, Z
	goto	display
	movlw	1
	movwf	days	; Wrap Days Counter
	goto	display
;
; Mode 1 -- just increment months
mode1
	incf	months, F
	movfw	months
	sublw	d'13'
	btfss	STATUS, Z
	goto	display
	movlw	1
	movwf	months	; Wrap Months Counter
	goto	display

;
; Update the display.
display
	incf	row, F
	movfw	row
	sublw	d'5'
	btfsc	STATUS, Z
	clrf	row
	movfw	row	; Get row to Display next
	addwf	PCL, F	; Branch based on the row
	goto	secs	; Row 0
	goto	mins	; Row 1
	goto	hour	; Row 2
	goto	day	; Row 3
	goto	month	; Row 4
intret
	movfw	saves	; Restore STATUS
	movwf	STATUS
	movfw	savew	; Restore W
	retfie		; Return from Interrupt service


secs			; Output Second
	movlw	0x1E
	movwf	PORTA
	movfw	seconds
	movwf	PORTB
	goto	intret

mins			; Output Minute
	movlw	0x1D
	movwf	PORTA
	movfw	minutes
	movwf	PORTB
	goto	intret

hour			; Output Hour
	movlw	0x1B
	movwf	PORTA
	movfw	hours
	movwf	PORTB
	goto	intret

day			; Output Day
	movlw	0x17
	movwf	PORTA
	movfw	days
	movwf	PORTB
	goto	intret

month			; Output Month
	movlw	0x0F
	movwf	PORTA
	movfw	months
	movwf	PORTB
	goto	intret

;
; Set up the baseline time, for first power-up.
	org	0x2100	; EEPROM address
	de	d'00',d'12',d'10',d'11'
	end
