; Binary LED Clock
	title "Binary LED Clock"
	LIST P=16F627
	#INCLUDE "P16F627.INC"
	__CONFIG _CP_OFF & _XT_OSC & _WDT_OFF & _PWRTE_ON & _MCLRE_OFF & _BODEN_OFF & _LVP_OFF

; Variables for delay loop
teller		EQU	070h
teller2	    EQU	071h

; Variables for time-keeping
sece	EQU	072h	; Second units
sect	EQU	073h	; Second tens
mine	EQU	07Bh	; Minute units
mint	EQU	07Ch	; Minute tens
hre		EQU	07Dh	; Hour units
hrt		EQU	07Eh	; Hour tens

; Variables for the one second timer
bres_hi		EQU	076h	; hi byte of our 24bit variable
bres_mid	EQU	077h	; mid byte
bres_lo		EQU	078h	; lo byte

; Variables to save register statusses during interrupt
temp_s		EQU	079h
temp_w		EQU	07Ah

		GOTO init
                ORG	0x4		; Choose starting adress

; Interrupt routine. This is called every 256 instructions.
isr		MOVWF	temp_w          ; save w register
          	MOVF	STATUS, W       ; W = STATUS
          	MOVWF	temp_s          ; save STATUS register

          	; Here comes the bookkeeping for the timer routine
bookkeeping	TSTF bres_mid		; first test for mid==0
		SKPNZ			; nz = no underflow needed
		DECF bres_hi,f		; z, so is underflow, so dec the msb

		DECFSZ bres_mid,f	; dec the mid byte (subtract 256)
		GOTO end_isr		; nz, so definitely not one second yet.

		TSTF bres_hi		; test hi for zero too
		SKPZ			; z = both hi and mid are zero, is one second!
		GOTO end_isr		; nz, so not one second yet.

		MOVLW 0x0F		; get msb value 
		MOVWF bres_hi		; load in msb
		MOVLW 0x42		; get mid value
		MOVWF bres_mid		; load in mid
		MOVLW 0x40		; lsb value to add
		ADDWF bres_lo,f		; add it to the remainder already in lsb
		SKPNC			; nc = no overflow, so mid is still ok

		INCF bres_mid,f		; c, so lsb overflowed, so inc mid

          	CALL continue		; A second has passed, increase the second count          	
          	
		; End of the interrupt routine
end_isr        	BCF	INTCON, T0IF	; clear the TMR0 flag bit
		MOVF	temp_s, W	; Put STATUS and W back where they belong
          	MOVWF	STATUS
          	SWAPF	temp_w, F
          	SWAPF	temp_w, W
          	RETFIE			; Return from interrupt


; Initialisation routine
init		BSF 	STATUS,5	; Choose bank 1
		CLRF	TRISA		; All pins of port A are outputs
		CLRF	TRISB		; All pins of port B are outputs...
		BSF	TRISB, 6
		BSF	TRISB, 7	; ...except for the last two
		MOVLW	b'10001000'	; Set the options (prescaler for watchdog timer)
		MOVWF	OPTION_REG
	        BCF 	STATUS,5	; Choose bank 0

		CALL initial_vals	; Set initial values
		
		MOVLW 0x0F		; get msb value 
		MOVWF bres_hi		; put in hi
		MOVLW 0x42 +1		; get mid value (note we added 1 to it)
		MOVWF bres_mid		; put in mid
		MOVLW 0x40		; get lsb value
		MOVWF bres_lo		; put in mid
		
		CLRF	INTCON
		BSF	INTCON, T0IE
		BSF	INTCON, GIE	; Set Timer interrupt

; Routine to show the numbers on the display
shownumbers		
		MOVLW	000h
		MOVWF	PORTB		; All LEDs off
		MOVF	sece,w		; Load sece into W
		MOVWF	PORTA		; Set the LEDs
		BSF	PORTB, 0	; Show the first row
		CALL delay		; Call our delay routine

		MOVLW	000h
		MOVWF	PORTB		; All LEDs off
		MOVF	sect,w		; Load sect in W
		MOVWF	PORTA		; Set the LEDs
		BSF	PORTB, 1	; Show the second row
		CALL delay
		
		MOVLW	000h
		MOVWF	PORTB		; All LEDs off
		MOVF	mine,w		; Load mine into W
		MOVWF	PORTA		; Set the LEDs
		BSF	PORTB, 2	; Show the third row
		CALL delay

		MOVLW	000h
		MOVWF	PORTB		; All LEDs off
		MOVF	mint,w		; Load mint into W
		MOVWF	PORTA		; Set the LEDs
		BSF	PORTB, 3	; Show the fourth row
		CALL delay

		MOVLW	000h
		MOVWF	PORTB		; All LEDs off
		MOVF	hre,w		; Load hre into W
		MOVWF	PORTA		; Set the LEDs
		BSF	PORTB, 4	; Show the fifth row
		CALL delay

		MOVLW	000h
		MOVWF	PORTB		; All LEDs off
		MOVF	hrt,w		; Load hrt into W
		MOVWF	PORTA		; Set the LEDs
		BSF	PORTB, 5	; Show the sixth row
		CALL delay

		GOTO shownumbers	; Just keep on showing those numbers -- the
					;   rest of the functionality is called from the interrupt
					;   routine.

; This routine is called whenever a second has passed.
continue	INCF	sece, 1		; Increase sece
		MOVF	sece, w
		ADDLW	0F6h		; sece > 9?
		SKPNC
		GOTO overflow_sece	; sece == 10, so increment sect
		RETURN

overflow_sece	CLRF	sece		; Set sece back to 0
		INCF	sect, 1		; Increase sect with 1
		MOVF	sect, w
		ADDLW	0FAh		; sect > 5?
		SKPNC
		GOTO overflow_sect	; sect == 5, so increment mine
		RETURN

overflow_sect	CLRF	sect		; Set sect back to 0
		CLRF	sece
		INCF	mine, 1		; Increase mine
		MOVF	mine, w
		ADDLW	0F6h		; mine > 9?
		SKPNC
		GOTO overflow_mine	; mine == 10, so increment sect
		RETURN

overflow_mine	CLRF	mine		; Set mine back to 0
		INCF	mint, 1		; Increase mint
		MOVF	mint, w
		ADDLW	0FAh		; mint > 5?
		SKPNC
		GOTO overflow_mint	; mint == 5, so increment hre
		RETURN

overflow_mint	CLRF	mint		; Reset mint to 0
incr_hre	INCF	hre, 1		; Increase hre
		MOVF	hre, w
		ADDLW	0FCh		; hre > 4?
		SKPNC
		GOTO check_hre		; hre > 4, so we need to check whether we need
					;   to overflow hrt
		RETURN

check_hre	MOVF	hrt, w
		ADDLW	0FEh		; hrt > 1?
		SKPNC
		GOTO	overflow_hre	; hrt == 2, hre == 5 -> overflow!
		MOVF	hre, w		; hrt == 1
		ADDLW	0F6h		; hre > 9?
		SKPNC
		GOTO overflow_hre	; hrt == 1, hre == 10 -> overflow!
		RETURN

overflow_hre	CLRF	hre		; Reset hre to 0
		INCF	hrt, 1		; Increase hrt
		MOVF	hrt, w
		ADDLW	0FDh		; hrt > 2?
		SKPNC
		GOTO overflow_hrt	; hrt == 2; overflow hrt (it's midnight!)
		RETURN

overflow_hrt	CLRF	sece		; Midnight, reset everything to 0!
		CLRF	sect
		CLRF	mine
		CLRF	mint
		CLRF	hre
		CLRF	hrt
		RETURN

; Sets the initial values. We start 9 seconds before midnight!
initial_vals	MOVLW	001h
		MOVWF	sece
		CLRF	sect
		CLRF	mine
		CLRF	mint
		CLRF	hre
		CLRF	hrt
		MOVLW	005h
		MOVWF	sect
		MOVLW	009h
		MOVWF	mine
		MOVLW	005h
		MOVWF	mint
		MOVLW	003h
		MOVWF	hre
		MOVLW	002h
		MOVWF	hrt
		RETURN

; Our delay loop. Waits roughly 1000 instructions, that's 1 msec
delay					
		MOVLW	00Ah		;10 times...
		MOVWF	teller
outerloop	MOVLW	064h		;100 times...
		MOVWF	teller2
innerloop	DECFSZ	teller2, 1
		GOTO innerloop
		DECFSZ	teller, 1
		GOTO outerloop
		RETURN

		END