	list b=4
;**************************************************************************
;Earl's Mods  7/2005 Nixie Tube  driven from PORT A and Port B
; CLOCK.ASM
;
; Displays time on a single Nixie display.  The timing source
; 4 MHz  crystal.

; *** Chime signal on RA3 ***

; A Nixie display is attached as follows:
;
;	RB0  0
;	RB1  1
;	RB2  2
;	RB3  3
;	RB4  4
;	RB5  5
;	RB6  6
;	RB7  Switch
;	RA0	7
;	RA1	8
;	RA2	9
;   RA3 Chime output
;
; Each output is connected via a resistor to a transistor base.
;  The common anode
; is connected to high plus voltage through resistor. 
; Holding botton down during power up runs a Nixie Test program 
;
; The time is displayed digit by digit over a four second period
; every 10 seconds and the display is blank otherwise.  
;
; A normally open switch is connected from RB7 to GND via a 470 ohm 
; resistor; no pull-up resistor is needed as an internal weak pull-up 
; is enabled by the program.  To set the clock press the switch when
; the appropriate digit is displayed; the digit can then be incremented 
; by pressing the switch repeatedly.  If the switch is open for about 
; 2 seconds the clock display is restarted allowing another digit to be 
; changed.  It is up to the user to put in sensible times as no checking 
; is done by the program.
;
;**************************************************************************

;#define	Mode24Hour	; Comment this line to use 12 hour mode clock

	ifdef __16F627
;		processor	16f627
		include		<p16f627.inc>
		__config	_XT_OSC & _WDT_OFF & _PWRTE_ON & _LVP_OFF
RAMBEG	EQU	0x20
	endif

	ifdef __16F627A
;		processor	16f627A
		include		<p16f627A.inc>
		__config	_XT_OSC & _WDT_OFF & _PWRTE_ON & _LVP_OFF
RAMBEG	EQU	0x20
	endif

	ifdef __16F628
;		processor	16f628
		include		<p16f628.inc>
		__config	_XT_OSC & _WDT_OFF & _PWRTE_ON & _LVP_OFF
RAMBEG	EQU	0x20
	endif

	ifdef __16F628A
;		processor	16f628A
		include		<p16f628A.inc>
		__config	_XT_OSC & _WDT_OFF & _PWRTE_ON & _LVP_OFF
RAMBEG	EQU	0x20
	endif

	ifdef __16F818
;		processor	16f818
		include		<p16f818.inc>
		__config	_XT_OSC & _WDT_OFF & _PWRTE_ON & _LVP_OFF
#define T0IF TMR0IF
RAMBEG	EQU	0x20
	endif

	ifdef __16F819
;		processor	16f819
		include		<p16f819.inc>
		__config	_XT_OSC & _WDT_OFF & _PWRTE_ON & _LVP_OFF
#define T0IF TMR0IF
RAMBEG	EQU	0x20
	endif

	ifdef __16C83
;		processor	16C83
		include		<p16c83.inc>
		__config	_XT_OSC & _WDT_OFF & _PWRTE_ON
RAMBEG	EQU	0x0C
	endif

	ifdef __16C84
;		processor	16c84
		include		<p16c84.inc>
		__config	_XT_OSC & _WDT_OFF & _PWRTE_ON
RAMBEG	EQU	0x0C
	endif

	ifdef __16F83
;		processor	16f83
		include		<p16f83.inc>
		__config	_XT_OSC & _WDT_OFF & _PWRTE_ON
RAMBEG	EQU	0x0C
	endif

	ifdef __16F84
;		processor	16f84
		include		<p16f84.inc>
		__config	_XT_OSC & _WDT_OFF & _PWRTE_ON
RAMBEG	EQU	0x0C
	endif

	ifdef __16F84A
;		processor	16f84A
		include		<p16f84A.inc>
		__config	_XT_OSC & _WDT_OFF & _PWRTE_ON
RAMBEG	EQU	0x0C
	endif

	ifdef __16F87
;		processor	16f87
		include		<p16f87.inc>
		__CONFIG _CONFIG1, _CP_OFF & _CCP1_RB3 & _WRT_PROTECT_OFF & _WDT_OFF & _BODEN_ON & _PWRTE_ON & _XT_OSC & _MCLR_ON & _LVP_OFF
		__CONFIG _CONFIG2, _IESO_OFF & _FCMEN_OFF
#define T0IF TMR0IF
RAMBEG	EQU	0x20
	endif

	ifdef __16F88
;		processor	16f88
		include		<p16f88.inc>
		__CONFIG _CONFIG1, _CP_OFF & _CCP1_RB3 & _WRT_PROTECT_OFF & _WDT_OFF & _BODEN_ON & _PWRTE_ON & _XT_OSC & _MCLR_ON & _LVP_OFF
		__CONFIG _CONFIG2, _IESO_OFF & _FCMEN_OFF
#define T0IF TMR0IF
RAMBEG	EQU	0x20
	endif

	ERRORLEVEL      -302    ; suppress bank selection messages

;#DEFINE _3_58MHZ                ; choose clock speed
#DEFINE _4_00MHZ

;#IFDEF  _3_58MHZ                ; number of ticks per second for 3.58 MHz:
;TCK0    EQU     .109            ; nominal
;TCK1    EQU     .111            ; every 10 secs
;TCK2    EQU     .113            ; every min
;TCK3    EQU     .115            ; every 10 mins
;TCK4    EQU     .114            ; every hour
;TCK5    EQU     .110            ; every 10 hours
;TCK6    EQU     .111            ; every 24 hours
;#ENDIF

;#IFDEF  _4_00MHZ                ; as above but for 4 MHz
;TCK0    EQU     .122
;TCK1    EQU     .123
;TCK2    EQU     .121
;TCK3    EQU     .123
;TCK4    EQU     .124
;TCK5    EQU     .125
;TCK6    EQU     .125
;#ENDIF



	CBLOCK  RAMBEG              ; define variables required
		TICKS                   ; decremented every tick (9.15 ms or 8.19 ms)
		SEGS                    ; one bit per segment
		SEC
		SEC10
		MIN
		MIN10
		HOUR
		HOUR10
		FRAME                   ; used to decide when to display time
		HHMM                    ; one bit per digit displayed
		COUNT                   ; scratch register
		DIGIT                   ; last digit displayed
		CHIME					; Number of chimes needed
		TCK0
		TCK1
		TCK2
		TCK3
		TCK4
		TCK5
		TCK6
	ENDC

#define OPTREG	OPTION_REG

	;*********************************;
	;         Initialization          ;
	;*********************************;

	ORG     0					; Turn pins to digital mode
INIT
	ifdef	CMCON				; If controller has Comparator module - turn it off
		banksel	CMCON
		movlw	0x07
		movwf	CMCON
		clrf	STATUS
	endif

	ifdef	ADCON1				; If controller has A/D module - turn pins to digital
		banksel	ADCON1
		movlw	0x06
		movwf	ADCON1
		clrf	STATUS
	endif

	ifdef	ANSEL				; If controller has A/D module - turn it off
		banksel	ANSEL
		clrf	ANSEL
		clrf	STATUS
	endif

		call	InitTicks

	    CLRF    SEC
		CLRF    SEC10
		CLRF    MIN
		CLRF    MIN10
		CLRF    HOUR
		movlw	D'1'
		movwf	HOUR10
		CLRF    FRAME
		CLRF    PORTA
		CLRF    PORTB
		CLRF	CHIME
		BSF     STATUS,RP0      ; select register bank 1
		CLRF    TRISA
		MOVLW   80H
		MOVWF   TRISB           ; only RB7 is an input
		MOVLW   4
		MOVWF   OPTREG          ; assign prescaler (1:32) to TMR0
		BCF     STATUS,RP0      ; reselect bank 0
	
		BTFSS   PORTB,7         ; switch open?
		GOTO    TEST         	; no, go to test mode
	
	;*********************************;
	;          Main Program           ;
	;*********************************;

MAIN    CALL    CLOCK           ; real-time clock algo called every second
		CALL    SHOW            ; display the time
		MOVF	CHIME,F			; check if chimes needed
		BZ		WAIT			; No Chime now
		BSF		PORTA,3			; Chime on
		DECF	CHIME,F
WAIT    BTFSS   INTCON,T0IF     ; wait for TMR0 to roll over
		GOTO    WAIT
		CLRF    INTCON
		CALL    CHKSW           ; check for button press
		MOVF	TICKS,W
		XORLW	D'20'			; Check for 20 ticks
		BTFSS	STATUS,Z
		GOTO	J1
		CLRF	PORTB			; Turn off Display at 100/120 ticks
		CLRF	PORTA
J1		DECFSZ  TICKS,F
		GOTO    WAIT
		GOTO    MAIN            ; get here every second


	;*********************************;
	;  Convert digit to segment form  ;
	;*********************************;

SEGTAB  ANDLW   0FH 
		ADDWF   PCL,F

		DT      .1,.2,.4,.8,.16,.32,.64,00,00,00,00 ; 0 to 6 Port B

SEGTABA
		ANDLW   0FH 
		ADDWF   PCL,F

		DT      00,00,00,00,0,0,0,.1,.2,.4,0 ; 7 to 9 Port A

	;*********************************;
	;  Real-time clock algorithm      ;
	;*********************************;

CLOCK
		MOVF   	TCK0,W        	; increment digits as appropriate
		MOVWF   TICKS           ; and define number of ticks per
		INCF    SEC,F           ; second for next second
		MOVF    SEC,W
		SUBLW   9
		BTFSC   STATUS,C
		 RETURN
	
		MOVF	TCK1,W			; per 10 sec
		MOVWF   TICKS
		CLRF    SEC				; clear seconds, add 10 seconds
		INCF    SEC10,F
		MOVF    SEC10,W
		SUBLW   5				; check for a 6
		BTFSC   STATUS,C
		 RETURN
	
		MOVF    TCK2,W			; per min
		MOVWF   TICKS
		CLRF    SEC10			; clear 10sec, add 1 minute
		INCF    MIN,F
		MOVF    MIN,W
		SUBLW   9				; check for 10
		BTFSC   STATUS,C
		 RETURN
	
		MOVF    TCK3,w			; per 10 minutes
		MOVWF   TICKS
		CLRF    MIN				; clear minute, add 10 minute
		INCF    MIN10,F
		MOVF    MIN10,W
		SUBLW   5
		BTFSC   STATUS,C
		 RETURN
	
	ifdef	Mode24Hour
		MOVF    TCK4,W
		MOVWF   TICKS
		CLRF    MIN10
		MOVF    HOUR10,W
		SUBLW   1
		BTFSC   STATUS,C
		 GOTO   INCHR
		INCF    HOUR,F
		MOVF    HOUR,W
		SUBLW   3
		BTFSC   STATUS,C
		goto 	SETCHIME
	
		MOVF    TCK6,w
		MOVWF   TICKS
		CLRF    HOUR
		CLRF    HOUR10
		goto 	SETCHIME

	else

		MOVF    TCK4,W			; each hour
		MOVWF   TICKS
		CLRF    MIN10			; clear 10 minute  add to hour
		MOVF    HOUR10,W		; check for over 10
		SUBLW   0
		BTFSC   STATUS,C
		 GOTO   INCHR			; less than 10
		INCF    HOUR,F			; 10 ->11  or 11--> 12
		MOVF    HOUR,W
		SUBLW   2				; check for 12
		BTFSC   STATUS,C
		goto 	SETCHIME
	
		MOVF    TCK6,W			; deal with 12 + 1 hour
		MOVWF   TICKS			; each 12 hour
		movlw	D'1'
		movwf	HOUR			; 1 o'clock
		CLRF    HOUR10
		goto 	SETCHIME

	endif

INCHR   INCF    HOUR,F			; Add to single digit hour
		MOVF    HOUR,W
		SUBLW   9				; did it go from 9 to 10 ?
		BTFSC   STATUS,C
		goto 	SETCHIME		; no, it's  less than 10

		MOVF    TCK5,W			; deal with 9 to 10 carry
		MOVWF   TICKS			; each 10 hour
		CLRF    HOUR
		INCF    HOUR10,F

SETCHIME
		MOVF	HOUR,W			; On hour change set chimes
		MOVWF	CHIME
		MOVF	HOUR10,F
		BZ		J2
		MOVLW	D'10'
		ADDWF	CHIME,F
J2		RETURN

	;*********************************;
	; Displays the time digit by digit;
	;*********************************;

SHOW    CLRF    HHMM            
		INCF    FRAME,F        	; increment place in frame
		MOVF    FRAME,W
		SUBLW   D'06'			; Display cycle time
		BTFSS   STATUS,C
		CLRF    FRAME
		MOVF    FRAME,W
		XORLW   1               
		BTFSC   STATUS,Z
		GOTO    DHOUR10        ; display 10s of hours when frame is 1
		XORLW   1^2
		BTFSC   STATUS,Z
		GOTO    DHOUR          ; display hour when frame is 2
		XORLW   2^3
		BTFSC   STATUS,Z
		GOTO    DMIN10         ; display 10s of mins when frame is 3
		XORLW   3^4
		BTFSC   STATUS,Z
		GOTO    DMIN           ; display mins when frame is 4 
		clrf	PORTB              ; otherwise blank display
		clrf	PORTA
		RETURN

DHOUR10 BSF     HHMM,3
		MOVF    HOUR10,W
		BTFSS	STATUS,Z		;check for leading zero
		GOTO    DISPLAY        
		RETURN					;skip display of zero

DHOUR   BSF     HHMM,2
		MOVF    HOUR,W
		GOTO    DISPLAY

DMIN10  BSF     HHMM,1
		MOVF    MIN10,W
		GOTO    DISPLAY

DMIN    BSF     HHMM,0
		MOVF    MIN,W           ; falls through to DISPLAY

	
	;*********************************;
	;       Displays digit in W       ;
	;*********************************;

DISPLAY
		MOVWF   DIGIT        	; save number to be displayed in DIGIT
		CALL    SEGTAB          ; convert to segment form
		MOVWF   PORTB           ; and display 0-6
		MOVF	DIGIT,W
		CALL	SEGTABA
		MOVWF   PORTA           ; and display  7-9
		RETURN

	;*********************************;
	; Checks for a switch press and   ;
	; updates digit if displayed      ;
	;*********************************;

CHKSW   BTFSC   PORTB,7         ; switch closed?
		RETURN                 ; no
		MOVF    HHMM,F
		BTFSC   STATUS,Z        ; digit displayed?
		RETURN                 ; no

INCDIG  INCF    DIGIT,F         ; DIGIT is the currently displayed number
		MOVF    DIGIT,W
		SUBLW   9
		BTFSS   STATUS,C
		CLRF    DIGIT
		movf	DIGIT,w
		btfsc	HHMM,1
		sublw	5
		btfsc	HHMM,3
	ifdef	Mode24Hour
		sublw	2
	else
		sublw	1
	endif
		BTFSS   STATUS,C
		CLRF    DIGIT
		MOVF    DIGIT,W
		CALL    DISPLAY
		CALL    DELAY           ; wait for switch to settle
CHKSW0  BTFSS   PORTB,7         ; switch open?
		GOTO    CHKSW0         ; no
		CALL    DELAY
		BTFSS   PORTB,7         ; still open?
		GOTO    CHKSW0         ; no
		MOVLW   D'20'
		MOVWF   COUNT
CHKSW1  BTFSS   PORTB,7         ; switch open?
		GOTO    INCDIG         ; no
		CALL    DELAY
		DECFSZ  COUNT,F
		GOTO    CHKSW1
		MOVF    DIGIT,W         ; switch was open for around 2 secs
		CLRF    FRAME  
		CLRF    SEC10
		CLRF    SEC
		BTFSC   HHMM,3          ; update correct digit
		MOVWF	HOUR10
		BTFSC   HHMM,2
		MOVWF	HOUR
		BTFSC   HHMM,1
		MOVWF	MIN10
		BTFSC   HHMM,0
		MOVWF   MIN

		movf	HOUR10,w
		call	Mull10
		addwf	HOUR,w
	ifdef	Mode24Hour
		sublw	.23
	else
		sublw	.12
	endif
		BTFSC   STATUS,C
		GOTO    MAIN            ; restart the program

		clrf	HOUR
	ifndef	Mode24Hour
		incf	HOUR,f
	endif
		clrf	HOUR10		
		GOTO    MAIN            ; restart the program

	;*********************************;
	;      Multiply by 10 routine     ;
	;*********************************;

Mull10
		movwf	FSR
		addwf	FSR,f
		bcf		STATUS,C
		rlf		FSR,f
		rlf		FSR,f
		addwf	FSR,f
		addwf	FSR,w
		return

	;*********************************;
	; Delay used by switch routine    ;
	;*********************************;

DELAY   MOVLW   D'12'           ; roughly 100ms delay
		MOVWF   TICKS
DEL1    BTFSS   INTCON,T0IF     ; wait for TMR0 to roll over
	 	GOTO    DEL1
		CLRF    INTCON
		DECFSZ  TICKS,F
	 	GOTO    DEL1
		RETURN

TEST	CLRF	COUNT			;exercise Nixie by cycling through
TEST1	MOVF	COUNT,W			;all numbers
		CALL	DISPLAY
		CALL	DELAY
		CALL	DELAY			;1 second delay
		INCF	COUNT,F
		MOVF	COUNT,W
		XORLW   D'10'			;Test for 10             
		BTFSS   STATUS,Z
		GOTO    TEST1
		GOTO	TEST   			;Reset COUNT to 0 

EESetAddr
		banksel	EEADR
		movwf	EEADR
		banksel	PORTA
		return

EERead
		banksel	EECON1
		bsf		EECON1,RD
		banksel	EEDATA
		movf	EEDATA,w
		banksel	EEADR
		incf	EEADR,f
		banksel	PORTA
		return


InitTicks
		movlw	0
		call	EESetAddr
		call	EERead
		movwf	TCK0
		call	EERead
		movwf	TCK1
		call	EERead
		movwf	TCK2
		call	EERead
		movwf	TCK3
		call	EERead
		movwf	TCK4
		call	EERead
		movwf	TCK5
		call	EERead
		movwf	TCK6
		return

		org	0x2100


#IFDEF  _3_58MHZ                ; number of ticks per second for 3.58 MHz:
;		in evry	 sec , 10 sec,  min, 10 min, hour, 10 hour, 24 hour
		de		 .109,   .111, .113,   .115, .114,    .110,  .111
#ENDIF

#IFDEF  _4_00MHZ                ; as above but for 4 MHz
;		in evry	 sec , 10 sec,  min, 10 min, hour, 10 hour, 24 hour
		de		 .122,   .123, .121,   .123, .124,    .125,  .125
#ENDIF

		END
