; PROGRAM: TACHO.SRC	Version 1.0

;	**************************
;	* Digital tacho 3 digits *
;	**************************

;	Copyright by WolfRam Technologies Inc. All rights reserved.
;	February 13th 1997 - February 1997

; *********************** Identify used controller ***************************

		device	pic16f84,xt_osc,wdt_off,pwrt_on,protect_off

; ************************** Port connections ********************************

DIGS		=	ra		; Data for digits (bits 0..3)
PULSE		=	ra.4		; Incoming pulse - this is an open
					; collector output, so it's better
					; to use it for an input
BL100		=	rb.4		; Disable 100s
CS100		=	rb.3		; Chip select for 100s
BL10		=	rb.2		; Disable 10s
CS10		=	rb.1		; Chip select for 10s
CS1		=	rb.0		; Chip select for 1s

; ******** Set RAM origin above special registers, declare variables *********

		org	12

tickl		ds	1		; Refreshment counter
tickh		ds	1		; Counter high byte
tach_count	ds	1		; Counter for pulses
last_tach	ds	1		; The last value read
delta		ds	1		; Register for difference calculations
last_delta	ds	1		; Difference history

bit_temp	ds	1		; Flags
DONE		=	bit_temp.0	; Handshake between prg and interrupt
puls_temp	=	bit_temp.1	; Status at measurement completion
no_tens		=	bit_temp.2	; No 10s display under 10 km/h

w_copy		ds	1		; [interrupt] Store for W
s_copy		ds	1		; [interrupt] Store for status reg

; ****************************** Reset vector ********************************

		org	0

		mov	PCLATH,#3	; Sets the jmp pc+w address high byte
					; to the end of the first 2K page.
		jmp	initialize

; *************************** Interrupt routine ******************************

		org	4

interrupt	mov	w_copy,w	; Make a copy of W
		mov	s_copy,status	; Make a copy of status
		jnb	T0IF,inter1	; Not timer1 overflow
		clrb	T0IF		; Clear the timer interrupt flag
					; With a refreshment period of 500 ms,
					; we have for each 1 km/h exactly
					; 1 pulse per refreshment period.
		inc	tickl		; Increment counter
		jnz	inter2
		inc	tickh
inter2		cjne	tickh,#7,inter1 ; Measurement not done
		cjne	tickl,#161,inter1
		setb	DONE
		clr	tickl		; Restart counter
		clr	tickh
inter1		mov	status,s_copy	; Restore status register
		swap	w_copy		; Prepare for swapped move
		mov	w,<>w_copy	; Swap/move to w, status unaffected
		reti			; Return to main program

; ***************************** Start program ********************************

initialize	clr	ra
		clr	rb
		clrb	RP1		; RP1 not used, set to 0
		setb	RP0		; Set page for bank1 registers
		mov	trisa,#10H	; 0..3 digits out, 4 is input: pulse
		clr	trisb		; Controls (set bit 7 as output to
					; avoid unwanted ITs)
		clrb	T0CS		; Use internal oscillator
		clrb	RP0		; Back to page 0
		clrb	T0IE		; No interrupts on timer overflow
		setb	GIE		; Enable interrupts
		clrb	T0IF		; Clear interrupt flag

startup		clr	tickl		; Clear counter
		clr	tickh
		clr	ra		; Display zero
		clr	rb		; Latch it
		clr	bit_temp	; Clear any info [interrupt]
		clr	tach_count	; No data
		clr	last_tach	; No history
		clr	last_delta
		mov	rb,#1FH		; Latched. Intelligent default: '0'
		setb	T0IE		; Enable timer interrupts
		clr	TMR0		; Restart timer

start1		jnb	DONE,start1	; Wait for the first round
		clrb	DONE		; Let's go!

; ********************** Normal operation of the tacho ***********************

tach		jb	DONE,tach1	; Measurement completed
		jnb	PULSE,tach	; Wait for the pulse
		inc	tach_count	; Count it
tach2		jb	DONE,tach1
		jb	PULSE,tach2	; Wait for the end of the pulse
		jmp	tach

tach1		movb	puls_temp,PULSE ; Store status
		clrb	DONE		; Clear indication
		cjne	tach_count,last_tach,tach3 ; Change since last value
					; Watch! No change does not mean that
					; the display is not refreshed. So if
					; new data is coming in (small change,
					; see below), it is not displayed at
					; the first run, only on the second.
		clr	last_delta	; Indicate no change
		jmp	tach5		; Redraw

tach3		mov	delta,last_tach	; Store last data in temporary register
		mov	last_tach,tach_count ; Store new data
		sub	delta,tach_count ; Compare new one with last one
		cje	delta,#1,tach6	; Small change - need to check history
		cje	delta,#0FFH,tach6 ; And again

		mov	last_delta,#128	; Indicate large difference
		jmp	tach5		; The change is big: redraw

tach6		cje	delta,last_delta,tach5 ; Continuous tendency: redraw
		cje	last_delta,#128,tach8 ; History shows changes: redraw
		mov	last_delta,delta ; Store new value
		jmp	tach7		; No drawing

tach8		mov	last_delta,delta ; Store new value
tach5		and	DIGS,#10H	; Clear indication
		clr	delta		; First digit
		setb	no_tens		; Assume small number
tach10		cjb	tach_count,#100,tach9
		inc	delta
		sub	tach_count,#100
		clrb	no_tens		; Above 100 km/h tens also displayed
		jmp	tach10		
tach9		test	delta		; Check if zero
		movb	BL100,Z		; If zero, blank display
		or	DIGS,delta	; Write 100s
		clrb	CS100		; Latch it
		clr	delta		; Next digit
		setb	CS100
tach12		cjb	tach_count,#10,tach11
		inc	delta
		sub	tach_count,#10
		jmp	tach12
tach11		and	DIGS,#10H
		test	delta		; Check if zero
		jnz	tach13		; Not zero: display it
		movb	BL10,no_tens	; If zero, disable: under 10 km/h
tach13		or	DIGS,delta
		clrb	CS10		; Latch
		nop
		setb	CS10
		and	DIGS,#10H
		or	DIGS,tach_count
		clrb	CS1
		clr	tach_count
		setb	CS1

tach7		jnb	puls_temp,tach	; Store the start by next pulse
		clrb	puls_temp
tach4		jb	PULSE,tach4	; Wait for the end of the pulse
		jmp	tach
