; FILE: Digital_clock.asm
; AUTH: Dhaminda Siriwardena
; DATE: 2nd May 2005
; DESC: A Digital Clock
; NOTE: Tested on PIC16F84-04/P.  

	list	p=16F84
	radix	hex



;----------------------------------------------------------------------
;	cpu equates (memory map)

tmr0			equ	0x01
pcl				equ	0x02
status 			equ	0x03

portA			equ	0x05		
portB			equ	0x06		

intcon			equ 0x0b

;----- User defined memory locations
counter1 		equ	0x10			; Incrementing counter lower 8 bits
counter2 		equ	0x10			; Incrementing counter higher 8 bits
freqLow			equ	0x12
freqHigh		equ	0x13

FreqDelay		equ	0x14

ClockMode		equ 0x15			; 0x03 for normal 0x01 or 0x02 for edit mode. 0x01 for minute edit and 0x02 for hour edit

digit			equ	0x16

delay			equ	0x17
MinTicks		equ	0x18
SecTicks		equ	0x19

status_Temp		equ	0x1a			; Interrupt temp storage
W_temp			equ	0x1b			; Interrupt temp storage

;----- CPU extended registers

option_reg		equ	0x81
trisa			equ 0x85
trisb			equ 0x86

;Flags of STATUS file
C				equ	0
DC				equ	1
Z				equ 2

;File types
W				equ	0
F				equ	1

;----------------------------------------------------------------------

		org	0x000
		goto 	start				; Start program

;----------------------------------------------------------------------
		org	0x04

int_service_routine				; Do Interrupt service
	
		movwf	W_temp 				; Copy W to TEMP register,
		movf	status, W			; Swap status to be saved into W
		movwf	status_Temp 		; Save status to STATUS_TEMP register

; Interrupt Service Routine
		btfss	intcon,2			; Check for Timer 0 overflow flag
		goto	no_INT

Timer0_Overflow						; Service Timer 0 overflow INT
		movfw	intcon
		andlw	b'11100000'			; Clear Time 0 INT Flag
		iorlw	b'00100000'			; Reset Timer0 INT
		movwf	intcon

		movlw	0x82				;Load time with 0xFF - 0x82 = 7D
		movwf	tmr0				;Load time with value

		incf	digit,F				; Point to next digit for the next call

		movfw	delay
		btfss	status,Z			; Check if delay is zero. If zero do not decf it
		decf	delay,F				; Decrease delay counter when TMR0 overflows

;Change Counter value
		decfsz	FreqDelay,F			; Do delay for seconds timming.
		goto	no_INT				; If not complete service next interrupt

		movlw	0xf9				; Set counter to divide by 125 insted of 250 so to divide by 2
		movwf	FreqDelay			

		movfw	ClockMode
		xorlw	0x03				; Check if both bits are ON
		btfss	status,Z			; If ON then stop Sec count display

		goto	CountSecs			; Skip to next section

		movfw	SecTicks			; Seconds blink LED status
		xorlw	0x10				; Invert current status
		movwf	SecTicks

CountSecs
		decfsz	MinTicks,F			; Count up to 60 Seconds for a minute
		goto	CheckEditButton

		movfw	ClockMode			; Check operation status
		xorlw	0x03				; Check if both bits are ON
		btfsc	status,Z			; If ON then stop Clock

		call	Clock_Counter

SkipClockCount
		movfw	freqLow				; Copy frequency count to output for display
		movwf	counter1

		movfw	freqHigh
		movwf	counter2

		movlw	0xf9				; Set counter to divide by 125 insted of 250 so to divide by 2
		movwf	FreqDelay

		movlw	0x3c				; Set 60 for 60Sec = 1 Min
		movwf	MinTicks

; Check for Edit mode switch on Port B
CheckEditButton
		btfsc	portB,7				; Check bit 7 status 
		goto  	no_INT

		decf	ClockMode,W			; Set clock in edit mode
		andlw	0x03
		btfsc	status,Z			; check if state is 00
		movlw	0x03				; Then revert to 0x03 for all ON
		movwf	ClockMode

		xorlw	0x03				; Check for Operational mode
		btfss	status,Z

		goto	SetPortA4Input		; Set port A to input

		banksel trisa				; Set Port A RA4 as an output
		clrf	trisa				; port A is output
		banksel portA
		goto	IntComplete

SetPortA4Input
		banksel trisa				; Set Port A RA4 as an input
		movlw	0x10				; Set RA4 as as input
		movwf	trisa				; port A is output
		banksel portA
		bsf		portA,4				; Clear data in port A bit 4

IntComplete
		movlw	0x00				; Clear Port B
		movf  	portB,W				; Read Port B and then clear RBIF flag

; End of Interrupt Service Routine		

no_INT
		movf 	status_Temp, W		; Swap nibbles in STATUS_TEMP register and place result into W
		movwf 	status				; Move W into STATUS register
									; (sets bank to original state)
		movf 	W_temp,W			; move W_TEMP and place result in W
	
		retfie						; Return from interrput
;End of Interrup service routine

;----------------------------------------------------------------------
start	
		clrwdt						; clear watchdog timer

;	bit 7: RBPU: PORTB Pull-up Enable bit
;		1 = PORTB pull-ups are disabled
;		0 = PORTB pull-ups are enabled (by individual port latch values)
;	bit 6: INTEDG: Interrupt Edge Select bit
;		1 = Interrupt on rising edge of RB0/INT pin
;		0 = Interrupt on falling edge of RB0/INT pin
;	bit 5: T0CS: TMR0 Clock Source Select bit
;		1 = Transition on RA4/T0CKI pin
;		0 = Internal instruction cycle clock (CLKOUT)
;	bit 4: T0SE: TMR0 Source Edge Select bit
;		1 = Increment on high-to-low transition on RA4/T0CKI pin
;		0 = Increment on low-to-high transition on RA4/T0CKI pin
;	bit 3: PSA: Prescaler Assignment bit
;		1 = Prescaler assigned to the WDT
;		0 = Prescaler assigned to TMR0
;	bit 2-0: PS2:PS0: Prescaler Rate Select bits
;
;		Bit Value 	TMR0 Rate 	WDT Rate
;		------------------------------------
; 			000		1 : 2		1 : 1
;		 	001		1 : 4		1 : 2
;			010		1 : 8		1 : 4
;			011		1 : 16		1 : 8
;			100		1 : 32		1 : 16
;			101		1 : 64		1 : 32
;			110		1 : 128		1 : 64
;			111		1 : 256		1 : 128

		movlw	b'11010100'			; assign prescaler, internal clock and divide by 256 

		banksel option_reg 
		movwf	option_reg
	
		banksel tmr0				; Set Tmr0 to initial value
		movlw	0x82				;Load time with 130 (255 - 125). Divide 
		movwf	tmr0				;Load time with value
	
		banksel trisb				; Configure Port Direction registers
		movlw	0x10				; Set RA4 as as input
		movwf	trisa				; port A is output

		movlw	0x80				; set w = 0
		movwf	trisb				; port B is output

		banksel portB
	
		movlw	0x03				; Switch all digits ON
		movwf	ClockMode			; Set clock on normal run mode
		
		clrf	freqLow
		clrf  	counter1			; Set counter1 to zero

		clrf	SecTicks
		clrf	digit

		movlw	0x12				; Set statup value to 12 O'clock
		movwf	freqHigh
		movwf	counter2			; 


; Freq = 1 Mhz Prescaler = 16 counter = 125
; Ref Freq = 1/(32*125) = 250
; 250 / 250 (Software) = 1Hz
		movlw	0xfa				; Set counter to 250
		movwf	FreqDelay

		movlw	0x3c				; Set 60 for 60Sec = 1 Min
		movwf	MinTicks

; INTCON	register
;		bit 7: GIE: Global Interrupt Enable bit
;			1 = Enables all un-masked interrupts
;			0 = Disables all interrupts
;		Note: For the operation of the interrupt structure, please refer
;		bit 6: EEIE: EE Write Complete Interrupt Enable bit
;			1 = Enables the EE write complete interrupt
;			0 = Disables the EE write complete interrupt
;		bit 5: T0IE: TMR0 Overflow Interrupt Enable bit
;			1 = Enables the TMR0 interrupt
;			0 = Disables the TMR0 interrupt
;		bit 4: INTE: RB0/INT Interrupt Enable bit
;			1 = Enables the RB0/INT interrupt
;			0 = Disables the RB0/INT interrupt
;		bit 3: RBIE: RB Port Change Interrupt Enable bit
;			1 = Enables the RB port change interrupt
;			0 = Disables the RB port change interrupt
;		bit 2: T0IF: TMR0 overflow interrupt flag bit
;			1 = TMR0 has overflowed (must be cleared in software)
;			0 = TMR0 did not overflow
;		bit 1: INTF: RB0/INT Interrupt Flag bit
;			1 = The RB0/INT interrupt occurred
;			0 = The RB0/INT interrupt did not occur
;		bit 0: RBIF: RB Port Change Interrupt Flag bit
;			1 = When at least one of the RB7:RB4 pins changed state (must
;			0 = None of the RB7:RB4 pins have changed state

		banksel intcon				; Change bank
		movlw	b'10100000' 		; Set Interrupt on level for TMR0 overflow interrupt flag 
		movwf	intcon				; Update INTCON Register

loop
		call	ShowTime			; Display Time

		movfw	ClockMode			; Get clock mode
		xorlw	0x03				; If ClockMode is 0x03 then  the Zero falg is set after XOR
		btfss	status,Z			; If ClockMode is 0x03 then loop Else edit
		goto	ClockEdit

		goto 	loop				; Enless loop

ClockEdit
		btfss	ClockMode,0			; Check miniute edit flag 
		goto	CheckHour			; 0x02 for Hour

		bcf		status,Z			; Clear Zero
		movfw	portA				; Read Port A to check port A bit 4 input
		andlw	0x10				; Check only PortA bit4 ON status
		btfss	status,Z			; Skip if Bit 4 set ON
		goto	loop

		;Increase Mins
		incf 	freqLow,F			; Increase Low byte

		movfw	freqLow				; Get Low nibble
		andlw	0x0f				
		bcf		status,DC			; Clear DC flag 
		sublw	0x09				; Miniute low digit
		btfsc	status,DC			; Check nibble carry
		goto	MinEditComplete		; All OK do Min edit delay
		
		movfw	freqLow				; Update Low nibble
		andlw	0xf0
		addlw	0x10				; Increase High nibble by one
		movwf	freqLow

		swapf	freqLow,W			; Swap nibbles
		bcf		status,DC
		sublw	0x05				; Check for 60 min range
		btfss	status,DC			
		clrf	freqLow				; If 60 is reached then clear to zero

MinEditComplete						; Display Freq values and display while delay
		movfw	freqLow
		movwf	counter1

		movfw	freqHigh
		movwf	counter2

		Call	SetupTimeDelay		; Call delay with display

		goto	loop				; Done with Min edit

CheckHour
		bcf		status,Z			; Clear Zero
		movfw	portA				; Read Port A to check port A bit 4 input
		andlw	0x10				; Check only PortA bit4
		btfss	status,Z
		goto	loop				; If nothing then get back to main loop

		;Increase Hours
		incf	freqHigh,F			; Increase hours

		movfw	freqHigh			; Select Low nibble of hour
		andlw	0x0f
		bcf		status,DC
		sublw	0x09				; Check for up 9 count on low nibble
		btfsc	status,DC			; If not then done with this nibble
		goto	HourEditComplete

		movfw	freqHigh			; Increase high nibble by one
		andlw	0xf0
		addlw	0x10			
		movwf	freqHigh

HourEditComplete
		Call	checkHours			; Check Hour 24 format

		movfw	freqLow				; Get a copy of the frequency
		movwf	counter1

		movfw	freqHigh
		movwf	counter2

		Call	SetupTimeDelay		; Delay with display

		goto	loop				; Get back to main loop
;---------------------------------------------------------------------------------
;	16 bit Counter
Clock_Counter
		incf 	freqLow,F			; Increase Low byte

		movfw	freqLow
		andlw	0x0f
		bcf		status,DC
		sublw	0x09				; Miniute low digit
		btfsc	status,DC			
		goto	no_nibble_carry
		
		movfw	freqLow
		andlw	0xf0
		addlw	0x10			
		movwf	freqLow

		swapf	freqLow,W	
		bcf		status,DC
		sublw	0x05				; Miniute high digit
		btfsc	status,DC			
		goto	no_nibble_carry
		clrf	freqLow
		incf	freqHigh,F

		movfw	freqHigh
		andlw	0x0f
		bcf		status,DC
		sublw	0x09				; Hour low digit
		btfsc	status,DC			
		goto	checkHours

		movfw	freqHigh
		andlw	0xf0
		addlw	0x10			
		movwf	freqHigh

checkHours
		movfw	freqHigh
		bcf		status,C			; Check for 24 hour format
		sublw	0x23				; Check for 24 hour clock format
		btfsc	status,C
		goto	no_nibble_carry
		movlw	0x00				; Set hour to '1'
		movwf	freqHigh
	

no_nibble_carry
		return

;----------------------------------------------------------------------
; Display value in 7 segments
ShowTime

		movfw	digit				; Get digit number

		andlw	0x03				;Select bit 0 and 1
		addwf	pcl,F				; Increament program counter

		goto	Digit1
		goto	Digit2
		goto	Digit3
		goto	Digit4

;Display Min Digit 1
Digit1
		movfw	counter1
		call	display				; Get display pattern in WREG
		btfss	ClockMode,0			; Check Minute display state
		movlw	0x00
		movwf	portB	
	
		movfw	SecTicks
		iorlw	b'11101110'			; Select Digit 4
		andlw	b'11111110'
		movwf	portA				; Send to portA
		return

;Display Min Digit 2
Digit2
		swapf	counter1,W
		call	display				; Get display pattern in WREG
		btfss	ClockMode,0			; Check Minute display state
		movlw	0x00
		movwf	portB	
	
		movfw	SecTicks
		iorlw	b'11101101'			; Select Digit 3
		andlw	b'11111101'
		movwf	portA				; Send to portA
		return
	
;Display Hour Digit 1
Digit3
		movfw	counter2
		call	display				; Get display pattern in WREG
		btfss	ClockMode,1			; Check Minute display state
		movlw	0x00
		movwf	portB	
	
		movfw	SecTicks
		iorlw	b'11101011'			; Select Digit 2
		andlw	b'11111011'
		movwf	portA				; Send to portA
	
		return

;Display Hour Digit 2
Digit4
		swapf	counter2,W
		andlw	0x0f
		btfss	status,Z
		call	display				; Get display pattern in WREG
		btfss	ClockMode,1			; Check Minute display state
		movlw	0x00
		movwf	portB				; Send to portB
	
		movfw	SecTicks
		iorlw	b'11100111'			; Select Digit 1
		andlw	b'11110111'
		movwf	portA				; Send to portA
	
		return

;----------------------------------------------------------------------
; converts the value in number to a 7 segment value
; Value passed in W Register
;
; Seven segment connections from Port B
;
;		            RB1
;		         --------
;		        |        |
;		   RB2  |        | RB0
;		        |        |
;		         --------
;		        |  RB3   |
;		   RB4  |        | RB6
;		        |        |
;		         --------        
;		            RB5
;
;----------------------------------------------------------------------;
;
display	
		andlw	0x0f				; Get lower nibble only
		addwf	pcl,F				; Increament program counter
	
		retlw	0x77				; 0
		retlw	0x41				; 1
		retlw	0x3b				; 2
		retlw	0x6b				; 3
		retlw	0x4d				; 4
		retlw	0x6e				; 5
		retlw	0x7e				; 6
		retlw	0x43				; 7
		retlw	0x7f				; 8
		retlw	0xcf				; 9
		retlw	0x5f				; A
		retlw	0x7c				; B
		retlw	0x36				; C
		retlw	0x79				; D
		retlw	0x3e				; E
		retlw	0x1e				; F
		retlw	0x08				; -

;----------------------------------------------------------------------
; Time Setup delay
; 
;
SetupTimeDelay
		
		movlw	0x7d				; Set Approx 1/5sec delay
		movwf 	delay				; Update delay to be changed by TMR0 INT
DelayLoop
		call	ShowTime			; Spend time displaying Time

		bcf		status,Z
		movfw	delay				; Check delay counter decremented by TMR0 INT
		btfss	status,Z			
		goto	DelayLoop			; If not then repeat counter
		
		return
	

		end
;----------------------------------------------------------------------
; at burn time, select:
;	memory uprotected
;	watchdog timer disabled
;	standard crystal (4 MHz)
;	power-up timer on
