;
        Title "SAE autoshifter."
;
        list P = 16F877A
;
        include "P16F877A.inc"  ; use definition file for 16F877A
;
; --------------------
; USER RAM DEFINITIONS
; --------------------
;
        CBLOCK 0x20   ; RAM starts at address 20h
gear
flag
rpm_flag
neutral_last
neutral
calibrated
NaHi
clkl
clkh
d1
d2
d3
Xhi
Xlo
        ENDC
;
        org 0x0000      ; start address = 0000h
		goto Initialize

; INITIALISE PORTS
; binary used to see inividual pin level

Initialize
		movlw b'00000000'
		movwf INTCON
		movlw 0x00
		movwf NaHi
		movwf 0x07
		movwf gear
        movlw b'00000000'       ; all port pins = low
        movwf PORTA
        movlw b'00000000'
        movwf PORTB
        movlw b'00000000'
        movwf PORTC
        movlw b'00000111'
        movwf PORTD
        movlw b'00000000'
        movwf PORTE
		
		;start a/d code
		bsf     STATUS,RP0      ;bank 1
        bcf     STATUS,RP1
        movlw   b'00001110'
        clrf    ADCON1          ;left justified, input an0
        bcf     STATUS,RP0      ;bank 0
        movlw   B'01000001'     ;Fosc/8 [7-6], A/D ch0 [5-3], a/d on [0]
        movwf   ADCON0

        bsf STATUS,RP0  ; set RAM Page 1 for TRIS registers
		bcf STATUS,RP1
		; INITIALISE PORTS
		; binary used to see individual pin IO status

        movlw b'11111111' ;1=input, 0=output
        movwf TRISA
        movlw b'11110010'
        movwf TRISB
        movlw b'00000000'
        movwf TRISC
        movlw b'00000000'
        movwf TRISD
        movlw b'00000000'
        movwf TRISE
		

        bcf STATUS,RP0  ; back to RAM page 0

		movlw 0x00
		movwf TMR1L
		movwf TMR1H
		movwf TMR1L
		movwf flag
		movwf rpm_flag
		movwf neutral
		movwf calibrated
		movwf neutral_last
		movlw b'00000001'
		movwf T1CON

		;delay to allow everything to settle
		call startup_delay

;main loop in which displays are updated	
Main 

rpm1	btfsc PORTB,5
		goto rpm1
rpm2	btfss PORTB,5
		goto rpm2
		movlw 0x00
		movwf TMR1L
		movwf TMR1H
		movwf TMR1L
rpm3	btfsc PORTB,5
		goto rpm3
rpm4	btfss PORTB,5
		goto rpm4
		movf TMR1L,W
		movwf clkl
		movf TMR1H,W
		movwf clkh

		btfss PORTB,4
		bsf calibrated,0 ; neutral must be low 
		btfss calibrated,0 
		goto button_done ; must not be calibrated 
 
		;check if we are in neutral
		btfss PORTB,4
		bsf neutral,0 ; must not be in neutral anymore 

		btfsc PORTB,4 
		bcf neutral,0 ; must not be in neutral anymore

		btfss neutral,0
		goto here 

		clrf gear	;set display to '0'
		clrf PORTD
		goto button_done

here
		btfss neutral_last,0 
		goto here2
		movlw 0x01
		movwf gear
		movwf PORTD

here2
		btfsc PORTB,5
		call rpm_check
		btfss PORTB,5
		bcf  rpm_flag,0
	
		;check if the buttons are pressed
		btfss PORTB,7
		goto button_up
		btfss PORTB,6
		goto button_down
		
button_done
		;clear the flag if needed
		btfss PORTB,7
		goto  $+3
		btfsc PORTB,6
		bcf flag,0

		movf neutral,W
		movwf neutral_last

		;update the rpm
		movlw 0x17 ; put 6000 in x
		movwf Xhi
		movlw 0x70
		movwf Xlo
		call compare_unsigned_16 ;test if count is less than 6000
		btfsc STATUS, C
		goto rpm_0 ; if count is greater than 6000 we have 0 RPM
		movlw 0x0b ; check 3000
		movwf Xhi
		movlw 0xb8
		movwf Xlo
		call compare_unsigned_16
		btfsc STATUS, C
		goto rpm_1000
		movlw 0x07 ; check 2000
		movwf Xhi
		movlw 0xd0
		movwf Xlo
		call compare_unsigned_16
		btfsc STATUS, C
		goto rpm_2000
		movlw 0x05 ; check 1500
		movwf Xhi
		movlw 0xdc
		movwf Xlo
		call compare_unsigned_16
		btfsc STATUS, C
		goto rpm_3000
		movlw 0x04 ; check 1200
		movwf Xhi
		movlw 0xb0
		movwf Xlo
		call compare_unsigned_16
		btfsc STATUS, C
		goto rpm_4000
		movlw 0x03 ; check 1000
		movwf Xhi
		movlw 0xe8
		movwf Xlo
		call compare_unsigned_16
		btfsc STATUS, C
		goto rpm_5000
		movlw 0x03 ; check 857
		movwf Xhi
		movlw 0x59
		movwf Xlo
		call compare_unsigned_16
		btfsc STATUS, C
		goto rpm_6000
		movlw 0x02 ; check 750
		movwf Xhi
		movlw 0xee
		movwf Xlo
		call compare_unsigned_16
		btfsc STATUS, C
		goto rpm_7000
		movlw 0x02 ; check 667
		movwf Xhi
		movlw 0x9b
		movwf Xlo
		call compare_unsigned_16
		btfsc STATUS, C
		goto rpm_8000
		movlw 0x02 ; check 600
		movwf Xhi
		movlw 0x58
		movwf Xlo
		call compare_unsigned_16
		btfsc STATUS, C
		goto rpm_9000
		movlw 0x02 ; check 545
		movwf Xhi
		movlw 0x21
		movwf Xlo
		call compare_unsigned_16
		btfsc STATUS, C
		goto rpm_10000
		movlw 0x01 ; check 500
		movwf Xhi
		movlw 0xf4
		movwf Xlo
		call compare_unsigned_16
		btfsc STATUS, C
		goto rpm_11000
		movlw 0x01 ; check 462
		movwf Xhi
		movlw 0xce
		movwf Xlo
		call compare_unsigned_16
		btfsc STATUS, C
		goto rpm_12000
		movlw 0x01 ; check 429
		movwf Xhi
		movlw 0xad
		movwf Xlo
		call compare_unsigned_16
		btfsc STATUS, C
		goto rpm_13000

		;low pass filter
		movlw 0x01 ; check 400
		movwf Xhi
		movlw 0x90
		movwf Xlo
		call compare_unsigned_16
		btfsc STATUS, C
		goto rpm_max
		goto rpm_impossible

rpm_0
		movf 	PORTC,W
		andlw	0xf0
		addlw   b'00000000'
		movwf 	PORTC
		goto led_finish
rpm_1000
		movf	PORTC,W
		andlw	0xf0
		addlw	b'00000001'
		movwf PORTC
		goto led_finish
rpm_2000
		movf	PORTC,W
		andlw	0xf0
		addlw	b'00000010'
		movwf PORTC
		goto led_finish
rpm_3000
		movf	PORTC,W
		andlw	0xf0
		addlw	b'00000011'
		movwf PORTC
		goto led_finish
rpm_4000
		movf	PORTC,W
		andlw	0xf0
		addlw	b'00000100'
		movwf PORTC
		goto led_finish
rpm_5000
		movf	PORTC,W
		andlw	0xf0
		addlw	b'00000101'
		movwf PORTC
		goto led_finish
rpm_6000
		movf	PORTC,W
		andlw	0xf0
		addlw	b'00000110'
		movwf PORTC
		goto led_finish
rpm_7000
		movf	PORTC,W
		andlw	0xf0
		addlw	b'00000111'
		movwf PORTC
		goto led_finish
rpm_8000
		movf	PORTC,W
		andlw	0xf0
		addlw	b'00001000'
		movwf PORTC
		goto led_finish
rpm_9000
		movf	PORTC,W
		andlw	0xf0
		addlw	b'00001001'
		movwf PORTC
		goto led_finish
rpm_10000
		movf	PORTC,W
		andlw	0xf0
		addlw	b'00001010'
		movwf PORTC
		goto led_finish
rpm_11000
		movf	PORTC,W
		andlw	0xf0
		addlw	b'00001011'
		movwf PORTC
		goto led_finish
rpm_12000
		movf	PORTC,W
		andlw	0xf0
		addlw	b'00001100'
		movwf PORTC
		goto led_finish
rpm_13000
		movf	PORTC,W
		andlw	0xf0
		addlw	b'00001101'
		movwf PORTC
		goto led_finish
rpm_max
		movf	PORTC,W
		andlw	0xf0
		addlw	b'00001110'
		movwf PORTC
		goto led_finish

rpm_impossible
		
led_finish
		movf gear,W
		sublw 0x01
		btfsc STATUS,Z
		call one_sug
		movf gear,W
		sublw 0x02
		btfsc STATUS,Z
		call two_sug
		movf gear,W
		sublw 0x03
		btfsc STATUS,Z
		call three_sug
		movf gear,W
		sublw 0x04
		btfsc STATUS,Z
		call four_sug
		movf gear,W
		sublw 0x05
		btfsc STATUS,Z
		call five_sug
		movf gear,W
		sublw 0x06
		btfsc STATUS,Z
		call six_sug
		
;do an a/d
ad_portc
		bsf     ADCON0,GO       ;Start A/D conversion
Wait
        btfsc   ADCON0,GO       ;Wait for conversion to complete
        goto    Wait

        movf    ADRESH,W        ;Write A/D result to W register
		addlw	0x6b		;overflow will occur if voltage on PORT RA0 > 2.9V
		btfsc   STATUS,C
		goto	ad_hot	

		movf    ADRESH,W        ;Write A/D result to W register
		addlw	0x94		;overflow will occur if voltage on PORT RA0 > 2.1V
		btfsc   STATUS,C
		goto	ad_ok

		goto	ad_cold		;if we get here PORT RA0 < 2.1V
	
ad_hot
		bsf	PORTC,7
		bcf PORTC,6
		bcf	PORTC,5
		goto ad_done
ad_ok
		bcf	PORTC,7
		bsf PORTC,6
		bcf	PORTC,5
		goto ad_done
ad_cold
		bcf	PORTC,7
		bcf PORTC,6
		bsf	PORTC,5
ad_done
        
        ;check oil pressure, change display appropriately
        btfsc PORTB,1 
        bsf	PORTD,4 ;pressure too low
        btfss PORTB,1	
        bcf	PORTD,4 ;pressure OK

		goto Main

button_up
		;if button still pressed, exit
		btfsc flag,0
		goto  done_up
		bsf   flag,0

		movf  gear,W
		sublw 0x06
		btfsc STATUS,Z
		goto  done_up
		
		movlw 0x01
		addwf gear,W
		movwf gear
		movwf PORTD
		
		bsf PORTD,7	; engine kill
		
		call e_kill_delay ; 25ms
		bsf PORTD,6 	; actuate up
		
		call actuate_delay ; 100ms
		bcf PORTD,6 ; stop actuation
		
		call e_kill_delay ; 25 ms
		;total shift time = 150ms	
		bcf PORTD,7; engine unkill
done_up
		goto button_done

button_down
		;if button still pressed, exit
		btfsc flag,0
		goto  done_down
		bsf   flag,0

		decfsz gear,W
		goto  $+2
		goto done_down
		movwf gear
		movwf PORTD

		bsf PORTD,7	; engine kill 
		
		call e_kill_delay ; 25ms
		bsf PORTD,5 	; actuate down

		call actuate_delay ; 100ms
		bcf PORTD,5 ; stop actuation

		call e_kill_delay ; 25ms
		;total shift time = 150ms
		bcf PORTD,7; engine unkill

done_down
		goto button_done

compare_unsigned_16:
		movf Xhi,w
		subwf clkh,w ; subtract Y-X
Are_they_equal:
		; Are they equal ?
		skpz
		goto results16
		; yes, they are equal -- compare lo
		movf Xlo,w
		subwf clkl,w	; subtract Y-X
results16:
		; if X=Y then now Z=1.
		; if X>Y then now C=0.
		; if X<=Y then now C=1.
		return

rpm_check
		btfsc rpm_flag,0
		goto rpm_done
		movf TMR1L,W
		movwf clkl
		movf TMR1H,W
		movwf clkh
		movlw 0x00
		movwf TMR1L
		movwf TMR1H
		movwf TMR1L
		bsf rpm_flag,0
rpm_done
		return 

one_sug
		;if gear=1 and rpm>9000, shift up
		movlw 0x02 
		movwf Xhi
		movlw 0x58
		movwf Xlo
		call compare_unsigned_16
		btfsc STATUS, C
		bcf PORTB,3 
		btfss STATUS,C
		bsf PORTB,3
		return

two_sug
		;if gear=2 and rpm>9500, shift up
		movlw 0x02 
		movwf Xhi
		movlw 0x58
		movwf Xlo
		call compare_unsigned_16
		btfsc STATUS, C
		bcf PORTB,3 
		btfss STATUS,C
		bsf PORTB,3

		;if gear=2 and rpm<4500, shift down
		movlw 0x05 
		movwf Xhi
		movlw 0xdc
		movwf Xlo
		call compare_unsigned_16 
		btfss STATUS, C
		bcf PORTB,2 
		btfsc STATUS,C
		bsf PORTB,2
		return

three_sug
		;if gear=3 and rpm>9000, shift up
		movlw 0x02 
		movwf Xhi
		movlw 0x58
		movwf Xlo
		call compare_unsigned_16
		btfsc STATUS, C
		bcf PORTB,3 
		btfss STATUS,C
		bsf PORTB,3

		;if gear=3 and rpm<4000, shift down
		movlw 0x05 
		movwf Xhi
		movlw 0xdc
		movwf Xlo
		call compare_unsigned_16 
		btfss STATUS, C
		bcf PORTB,2 
		btfsc STATUS,C
		bsf PORTB,2
		return

four_sug
		;if gear=4 and rpm>9500, shift up
		movlw 0x02 
		movwf Xhi
		movlw 0x58
		movwf Xlo
		call compare_unsigned_16
		btfsc STATUS, C
		bcf PORTB,3 
		btfss STATUS,C
		bsf PORTB,3

		;if gear=4 and rpm<3750, shift down
		movlw 0x05 
		movwf Xhi
		movlw 0xdc
		movwf Xlo
		call compare_unsigned_16 
		btfss STATUS, C
		bcf PORTB,2 
		btfsc STATUS,C
		bsf PORTB,2
		return

five_sug
		;if gear=5 and rpm>8000, shift up
		movlw 0x02 
		movwf Xhi
		movlw 0x58
		movwf Xlo
		call compare_unsigned_16
		btfsc STATUS, C
		bcf PORTB,3 
		btfss STATUS,C
		bsf PORTB,3
		
		;if gear=5 and rpm<4000, shift down
		movlw 0x05 
		movwf Xhi
		movlw 0xdc
		movwf Xlo
		call compare_unsigned_16 
		btfss STATUS, C
		bcf PORTB,2 
		btfsc STATUS,C
		bsf PORTB,2
		return

six_sug
		;if gear=6 and rpm<3000, shift down
		movlw 0x05 
		movwf Xhi
		movlw 0xdc
		movwf Xlo
		call compare_unsigned_16 
		btfss STATUS, C
		bcf PORTB,2 
		btfsc STATUS,C
		bsf PORTB,2
		return

startup_delay
		;2 second delay to let signals settle
		movlw	0x11
		movwf	d1
		movlw	0x5D
		movwf	d2
		movlw	0x05
		movwf	d3
Delay_0
		decfsz	d1, f
		goto	$+2
		decfsz	d2, f
		goto	$+2
		decfsz	d3, f
		goto	Delay_0

		;4 cycles
		goto	$+1
		goto	$+1
		return
	
e_kill_delay
		;25ms delay
		movlw	0x4F
		movwf	d1
		movlw	0xC4
		movwf	d2
Delay_1
		decfsz	d1, f
		goto	$+2
		decfsz	d2, f
		goto	Delay_1

		;2 cycles
		goto	$+1
		return
		
actuate_delay
		;100ms delay
		movlw	0x03
		movwf	d1
		movlw	0x18
		movwf	d2
		movlw	0x02
		movwf	d3
Delay_2
		decfsz	d1, f
		goto	$+2
		decfsz	d2, f
		goto	$+2
		decfsz	d3, f
		goto	Delay_2
		
		;6 cycles
		goto	$+1
		goto	$+1
		goto	$+1
		return		

        end