;**************************************************************************
;
; MODIFIED BY JON STANLEY FOR 24-HOUR CLOCK MODE
;
; Displays time on a single 7-segment display.  The timing source
; can be an NTSC colour burst crystal (3.579454 MHz) or a 4 MHz
; crystal.
;
; A common anode 7-segment LED display is attached as follows:
;
;    RA0 segment a
;    RA1 segment b
;    RA2 segment c
;    RA3 segment d
;    RA4 segment e
;    RB4 segment f
;    RB5 segment g
;
; Each segment is connected via a 470 ohm resistor.  The common anode
; is connected directly to VDD.  The strange allocation of port bits
; to segments allows RB6 and RB7 to be dedicated to in-circuit serial 
; programming.
;
; The time is displayed digit by digit over a four second period
; every 15 seconds and the display is blank otherwise.  Four LEDs are 
; connected from RB3-RB0 to GND via 470 ohm resistors; these are used
; to indicate the significance of the displayed digit: 10s of hours - 
; hours - 10s of minutes - minutes.
;
; 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.
;
; To permit the PIC to be programmed in-circuit the /MCLR pin is
; connected to VDD via a 1k resistor in series with a signal diode 
; (e.g. 1N4148) with anode connected to VDD.  
;
;
; Copyright (C) 1998 David Tait (david.tait@man.ac.uk)
;
;**************************************************************************

 ifdef	__16C84
	LIST            P=16C84 ; no need to change this for the 16C84
	#include <p16C84.inc>       ; processor specific variable definitions
	ERRORLEVEL      -302    ; suppress bank selection messages
	__CONFIG        3FF9H   ; XT oscillator (and PWRTE if 16C84)
 #define	RamBeg	0x0C
 endif

ifdef	__16F84A
	LIST            P=16F84A ; no need to change this for the 16F84A
	#include <p16f84A.inc>       ; processor specific variable definitions
	ERRORLEVEL      -302    ; suppress bank selection messages
	__CONFIG        3FF9H   ; XT oscillator (and PWRTE if 16F84A)
 #define	RamBeg	0x0C
 endif

 ifdef	__16F84
	LIST            P=16F84 ; no need to change this for the 16C84
	#include <p16f84.inc>       ; processor specific variable definitions
	ERRORLEVEL      -302    ; suppress bank selection messages
	__CONFIG        3FF9H   ; XT oscillator (and PWRTE if 16F84)
 #define	RamBeg	0x0C
 endif

 ifdef	__16F628
	LIST            P=16F628 ; no need to change this for the 16F628
	#include <p16f628.inc>       ; processor specific variable definitions
	ERRORLEVEL      -302    ; suppress bank selection messages
	__CONFIG        3F01H   ; XT oscillator, LVP off (and PWRTE if 16F628)
 #define	RamBeg	0x70
 endif

 ifdef	__16F628A
	LIST            P=16F628A ; no need to change this for the 16F628A
	#include <p16f628A.inc>       ; processor specific variable definitions
	ERRORLEVEL      -302    ; suppress bank selection messages
	__CONFIG        3F01H   ; XT oscillator, LVP off (and PWRTE if 16F628A)
 #define	RamBeg	0x70
 endif

 #DEFINE _3_58MHZ                ; choose clock speed
;#DEFINE _4_00MHZ

;PCL     EQU     2               ; registers are defined here to make this
;STATUS  EQU     3               ; program self contained.
;PORTA   EQU     5
;PORTB   EQU     6
;INTCON  EQU     0BH
;TRISA   EQU     85H
;TRISB   EQU     86H
;OPTREG  EQU     81H
;
;C       EQU     0               ; bits in STATUS
;Z       EQU     2
;RP0     EQU     5
;
;T0IF    EQU     2               ; bit in INTCON
;

 #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: "-gfedcba"
	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

	ENDC


	;*********************************;
	;         Initialisation          ;
	;*********************************;

	ORG     0

INIT
    CLRF    SEC
	CLRF    SEC10
	CLRF    MIN
	CLRF    MIN10
	movlw	D'2'		; 12:00 initialized here!
	movwf	HOUR
	movlw	D'1'
	movwf	HOUR10
	CLRF    FRAME
 ifdef	CMCON
	movlw	0x07
	movwf	CMCON
 endif
	CLRF    PORTA
	CLRF    PORTB
	BSF     STATUS,RP0      ; select register bank 1
	CLRF    TRISA
	MOVLW   80H
	MOVWF   TRISB           ; only RB7 is an input
	MOVLW   4
	MOVWF   OPTION_REG      ; 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
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
	MOVLW   0x30            ; Turn off Display at 100/120 ticks
	MOVWF   PORTB
	MOVLW   0x1F
	MOVWF   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      0x40,0x79,0x24,0x30,0x19,0x12,0x02,0x78,0x00,0x10
	DT      0x7F,0x7F,0x7F,0x7F,0x7F,0x7F


	;*********************************;
	;  Real-time clock algorithm      ;
	;*********************************;

CLOCK
    MOVLW   TCK0            ; 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

	MOVLW   TCK1
	MOVWF   TICKS
	CLRF    SEC
	INCF    SEC10,F
	MOVF    SEC10,W
	SUBLW   .5
	BTFSC   STATUS,C
	 RETURN

	MOVLW   TCK2
	MOVWF   TICKS
	CLRF    SEC10
	INCF    MIN,F
	MOVF    MIN,W
	SUBLW   .9
	BTFSC   STATUS,C
	 RETURN

	MOVLW   TCK3
	MOVWF   TICKS
	CLRF    MIN
	INCF    MIN10,F
	MOVF    MIN10,W
	SUBLW   .5
	BTFSC   STATUS,C
	 RETURN

	MOVLW   TCK4
	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
	  RETURN

	MOVLW   TCK6
	MOVWF   TICKS
	CLRF    HOUR
	CLRF    HOUR10
	RETURN

INCHR
    INCF    HOUR,F
	MOVF    HOUR,W
	SUBLW   .9
	BTFSC   STATUS,C
	 RETURN

	MOVLW   TCK5
	MOVWF   TICKS
	CLRF    HOUR
	INCF    HOUR10,F
	RETURN



	;*********************************;
	; Displays the time digit by digit;
	;*********************************;

SHOW
    CLRF    HHMM            
	INCF    FRAME,F         ; increment place in frame
	MOVF    FRAME,W
	SUBLW   D'08'		    ; 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 
	MOVLW   0x30              ; otherwise blank display
	MOVWF   PORTB
	MOVLW   0x1F
	MOVWF   PORTA
	RETURN

DHOUR10
    BSF     HHMM,3
	MOVF    HOUR10,W
	BTFSS	STATUS,Z	; check for leading zero
	GOTO    DISPLAY
	RETURN			; skip display of leading 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   SEGS            ; and store
	ANDLW   1FH             ; extract PORTA segments
	MOVWF   PORTA           ; and display
	RRF     SEGS,W
	ANDLW   30H             ; extract PORTB segments
	IORWF   HHMM,W
	MOVWF   PORTB           ; and display
	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		; If DIGIT incremented over 9
	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
	GOTO    MAIN            ; restart the program


	;*********************************;
	; 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 Numitron by cycling through
		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    TEST+1
		GOTO	TEST   		; Reset COUNT to 0 
		END

