; -----------------------------------
; Drehzahlmesser.asm
;
; Steuerprogramm für optischen Drehzahlmesser
;
; Misst über einen Fototransistor die Drehzahl z.B. eines Modellbaumotors
; Kontaktlos, optische Abtastung
; Messung erfolgt durch Auswertung der Zeit zwischen zwei Impulsen via capture Modul mit einer Auflösung von 2,66us
; Einstellbare Anzahl der Impulse pro Umdrehung von 1 bis 4, um z.B. auch mit 2-, 3- oder 4-Blatt 
; Luftschrauben ohne Umrechnung zu funktionieren.
; automatische Bereichsumschaltung 0..9999 und 10.000 bis 37.500
; Handheld-Gehäuse, batteriebetrieben, 4-stelliges 7-segm. Display
;
; (c) Stephan Asmus 06/2019
; info@sasmus.de
;
; Das Programm oder Teile davon dürfen für private, nicht-kommerzielle
; Anwendungen weiterverwendet werden.
; Der ursprüngliche Autor ist dabei zu erwähnen
; -----------------------------------
; PIC 16F690
; -----------------------------------
; Log:
; 27.06.2019 Start, outline, display & key working
; 30.06.2019 ready, 1st release
;
;
; Used Controller: PIC 16F690
; Pins & Ports:
;
;   Port C:
;   0: Segment g
;   1: Segment f
;   2: Segment e
;   3: Segment a
;   Port B:
;   4: Segment d
;   5: Segment c
;   6: Segment b
;   7: Dots 
;
;   
;   RA2:  Driver Display 1
;   RC4:  Driver Display 2
;   RC6:  Driver Display 3
;   RC7:  Driver Display 4
;
;   RA3: Switch
;   CCP1/RC5: Sensor input
;
; see schematic for more Details
;
; Fosc=12MHz ceramic oscillator


	LIST   P=PIC16F690

	#include <p16f690.inc>

    errorlevel  -302		;No bank select warnings
    errorlevel  -306		;No page select warnings

	__config (_HS_OSC & _WDT_OFF & _PWRTE_OFF & _MCLRE_OFF & _CP_OFF & _BOD_OFF & _IESO_OFF & _FCMEN_OFF)
;----------------------------------------------------------------
;variables
w_tmp		EQU	0x20    ; used for context saving Register "W"
status_tmp	EQU	0x21    ; used for context saving Register "Status"
pclath_tmp	equ	0x22		; used for context saving Register "PCLATH"
pclath_save	equ 0x23		; used to save pclath within computed gotos
	cblock 0x24
	d1				;Delay-Variable 1
	d2				;Delay-Variable 2
	d3				;Delay-Variable 3

    xhi             ;for math routines
    xlo
    yhi
    ylo
	
    f0              ;used by conversion routines & add24
	f1
	f2
	g0
	g1
	g2
	
	digit1			;digits after conversion to BCD
	digit2
	digit3
	digit4
	digit5
	digit6
	digit7
	digit8
	bitcnt			;used by conversion routine
	digcnt

	ppu			;pulses per rotation

	const0		;holds constant (dividend) for calculating rpm
	const1
	const2
	const3
	
	divid0		;dividend for calculating rpm
	divid1	
	divid2
	divid3
	divis0		;divisor for calculation rpm
	divis1
	remdrL		;used by division routine
	remdrH
	
	old00			;old values (used for interpolating)
	old01
	old02
	old10
	old11
	old12
	old20
	old21
	old22
	
    disp1           ; holds 4 Display positions (active on display): left
    disp2
    disp3
    disp4			;right
    position        ;Position of Display (0..3)

	capture_l		;used to save captured data
	capture_h
	
    tickcounter_l   ;counter für ISR loop
    tickcounter_h

	
    keydata         ;holds pressed key

    flags           ;holds diverse flags (see definitions)

	eprom_adr		;eeprom Adress tmp
	eprom_dat		;eeprom data tmp

	endc
;----------------------------------------------------------------
;constants & definitions
#define Key	PORTA,3	;Key
#define f_high flags,0 ;indicates that frequency is too high
#define f_low  flags,1 ;indicates that frequency is too low
#define config_changed  flags,2 ;indicates if config has to be saved to eeprom
#define new_value	flags,3	;indicates that a new valid value for rpm arrived


;----------------------------------------------------------------
;macro definitions
;----------------------------------------------------------------
;set to Register Bank 0
SET_BANK0 MACRO
	bcf STATUS, RP0
	bcf STATUS, RP1
	ENDM
;set to Register Bank 1
SET_BANK1 MACRO
	bsf	STATUS, RP0
	bcf	STATUS, RP1
	ENDM
;set to Register Bank 2
SET_BANK2 MACRO
	bcf	STATUS, RP0
	bsf	STATUS, RP1
	ENDM
;strout Macro to output string on display
strout	macro	stringaddr
    movlw   high stringaddr
    movwf   PCLATH
    call    stringaddr
    movwf   disp1
    call    stringaddr+1
    movwf   disp2
    call    stringaddr+2
    movwf   disp3
    call    stringaddr+3
    movwf   disp4
    movlw   high $
    movwf   PCLATH
		endm
; **********************************************************************
;Program Start
; **********************************************************************
	org 0
	goto    INIT
	
	ORG		0x004           ; interrupt vector location
	movwf	w_tmp          ; save off current W register contents
	swapf	STATUS,W        ; move status register into W register
	clrf		STATUS          ; select Bank 0
	movwf	status_tmp     ; save off contents of STATUS register
	movf		PCLATH,w
	movwf	pclath_tmp
	clrf		PCLATH

	btfsc	PIR1,CCP1IF		;If capture module caused the interrupt...
	call		CCP1_ISR
	
	btfsc	PIR1,TMR1IF		;If timer 1 overflow...
	call		Timer1_ISR
	
	btfsc   INTCON,T0IF     ; If Timer 0 caused the interrupt, handle it.
	call    Timer0_ISR		; 

	movf		pclath_tmp,w
	movwf	PCLATH
	swapf   status_tmp,w     ; retrieve copy of STATUS register
	movwf   STATUS            ; restore pre-isr STATUS register contents
	swapf   w_tmp,f
	swapf   w_tmp,w          ; restore pre-isr W register contents
	retfie                    ; return from interrupt

; *********************************************
; Timer 1 Interrupt handler.
; Timer 1 has overflowed
; *********************************************
Timer1_ISR
	bsf		f_low		;indicate that frequency is too low to measure
	bcf		new_value		;clear other flags
	bcf		f_high
	clrf		TMR1L		;clear timer 1
	clrf		TMR1H
	bcf		PIR1,TMR1IF	;clear interrupt flag
	return
; *********************************************
; CCP1 Interrupt handler.
; CCP1 captured new data
; *********************************************
CCP1_ISR

	btfss	f_low		;if last status was f_low, then ignore this capture to suppress false values
	bsf		new_value		;indicate new value
	bcf		f_high		;clear flag f_high (not checked yet)
	btfsc	f_low		;ignore if previously low
	goto		ccp1_end
	
	clrf		TMR1L		;clear timer 1
	clrf		TMR1H
	movf		CCPR1L,w		;save capture data
	movwf	capture_l
	movf		CCPR1H,w
	movwf	capture_h
	;now check if frequency is too high
	movf		ppu,w		;get current ppu
	xorlw	d'1'			;check if ppu is 1
	skpz
	goto		ccp1_1
	movf		capture_h,w
	movwf	xhi
	movf		capture_l,w
	movwf	xlo
	movlw	0x58
	movwf	ylo
	movlw	0x02
	movwf	yhi
	call		compare_unsigned_16
	skpnc
	bsf		f_high
	goto		ccp1_end
ccp1_1
	xorlw	d'1'^d'2'	;check if ppu is 2
	skpz
	goto		ccp1_2
	movf		capture_h,w
	movwf	xhi
	movf		capture_l,w
	movwf	xlo
	movlw	0x2c
	movwf	ylo
	movlw	0x01
	movwf	yhi
	call		compare_unsigned_16
	skpnc
	bsf		f_high
	goto		ccp1_end	
ccp1_2
	xorlw	d'2'^d'3'	;check if ppu is 3
	skpz
	goto		ccp1_3
	movf		capture_h,w
	movwf	xhi
	movf		capture_l,w
	movwf	xlo
	movlw	0xc8
	movwf	ylo
	movlw	0x00
	movwf	yhi
	call		compare_unsigned_16
	skpnc
	bsf		f_high
	goto		ccp1_end	
ccp1_3
	movf		capture_h,w
	movwf	xhi
	movf		capture_l,w
	movwf	xlo
	movlw	0x96
	movwf	ylo
	movlw	0x00
	movwf	yhi
	call		compare_unsigned_16
	skpnc
	bsf		f_high
ccp1_end
	btfsc	f_high		;f_high set after checks?
	bcf		new_value		;yes, clear flag again (cause it is wrong now)

	bcf		PIR1,CCP1IF	;clear Interrupt-Flag
	bcf		f_low		;clear low flag
	return
	
; *********************************************
; Timer 0 Interrupt handler.
; Timer 0 has overflowed
; *********************************************
Timer0_ISR

    incf    position,f	;next dislay position
    movlw   0x04			;already 5?
    xorwf   position,w
    btfsc   STATUS,Z
    clrf    position		;yes, then start at 0

    movf    position,w
    xorlw   0x00
    skpnz
    goto    is_1
    xorlw   0x01^0x00
    skpnz
    goto    is_2
    xorlw   0x02^0x01
    skpnz
    goto    is_3
    goto    is_4

is_1
    bcf		PORTC,7		;that was the last one switched on
    movf    disp1,w
	movwf	PORTB		;and set Port (RB4..RB7)
	andlw	0x0f			;mask for Rc0..RC3
	movwf	PORTC		;and set Port
    bsf		PORTA,2		;switch on transistor
    goto		disp_loaded
is_2
    bcf	    PORTA,2		;that was the last one switched on
    movf    disp2,w
	movwf	PORTB		;and set Port (RB4..RB7)
	andlw	0x0f			;mask for Rc0..RC3
	movwf	PORTC		;and set Port
    bsf		PORTC,4		;switch on transistor
    goto    disp_loaded
is_3
    bcf	    PORTC,4		;that was the last one switched on
    movf    disp3,w
	movwf	PORTB		;and set Port (RB4..RB7)
	andlw	0x0f			;mask for Rc0..RC3
	movwf	PORTC		;and set Port
    bsf		PORTC,6		;switch on transistor
    goto    disp_loaded
is_4
    bcf	    PORTC,6		;that was the last one switched on
    movf    disp4,w
	movwf	PORTB		;and set Port (RB4..RB7)
	andlw	0x0f			;mask for Rc0..RC3
	movwf	PORTC		;and set Port
    bsf		PORTC,7		;switch on transistor

disp_loaded
    
    incf    tickcounter_l,f     ;increment low-counter (256 ticks ca. 700msec)
    skpz                        ;overflow?
    goto    tmr0end             ;no, then end
    incf    tickcounter_h,f     ;yes, then increment high-couner (1Tick ca. 700ms)
    movf    tickcounter_h,w
    xorlw   0x0A                ;10 secs over?
    skpz
    goto    tmr0end             ;no, then end
    ;else...
    clrf    tickcounter_h       ;clear counter

tmr0end
	BCF 	INTCON,T0IF		; Clear Timer0 flag and continue.
    return
;------------------------------------------------------------------
;initializes Processor
;------------------------------------------------------------------
INIT
	SET_BANK1	 		;select Register Page 1
    clrf    TRISA           ;Port A all output (RA3 always input)
    clrf    TRISB           ;Port B all output
	clrf    TRISC           ;Port C all output
	;except RC5/CCP1:
	bsf     TRISC,5			;as an input (CCP1)
    SET_BANK2
    ;configure analog I/O
	clrf 	ANSEL		;all digital I/O
	clrf		ANSELH
	SET_BANK0

    ;initialise Timer 0
	;we are in Bank 0...
	clrwdt				;Routine from Datasheet Microchip
	clrf		TMR0
	bsf		STATUS, RP0	;Switch to BANK 1
	movlw	b'11000100'		;Pullup disable, Prescaler to TMR0, Prescaler 1:32
	movwf   OPTION_REG
	SET_BANK0
	
	;initialise Timer1
	movlw	b'00110001'	;Prescale 1:8, internal clock, enable timer
	movwf	T1CON
	clrf		TMR1L
	clrf		TMR1H
	
	;initialise capture module
	movlw	b'00000101'	;capture mode, every rising edge
	movwf	CCP1CON
	
	;----now set reasonable values to variables
    clrf    position
    clrf    disp1
    clrf    disp2
    clrf    disp3
    clrf    disp4

    clrf    tickcounter_l
    clrf    tickcounter_h
    clrf    flags
    
    call    load_prefs
	call		set_const

	;---
	SET_BANK1
	clrf		PIE1				;all disable
	bsf		PIE1,CCP1IE		;enable CCP1
	bsf		PIE1,TMR1IE		;enable TMR1
	SET_BANK0
	clrf 	TMR0
	clrf		INTCON			;initialize Interrupts
	bsf		INTCON,T0IE		;enable Timer0 interrupts
	clrf		PIR1				;clear pending peripheral interrupt flags
	bsf		INTCON,PEIE		;enable peripheral interrupts
	bsf		INTCON,GIE		;enable global interrupts
							;now timer 0 & 1 fires

;#############################################################################
;here to start main routine###################################################
;#############################################################################
main
	call		keycheck			;key pressed?
    skpnz					;yes, then handle menu etc
    goto		main1			;no, then go on 
    call		handle_menu		;else goto menu and handle it
    call		waitforrelease	;wait for key release
    btfsc	config_changed	;do we have a new config?
    call		save_prefs		;yes, save it to eeprom
	call		set_const

main1
	btfss	f_low			;indicate message if Flag on
	goto		main2
	strout	msg_low
main2
	btfss	f_high			;indicate message if Flag on
	goto		main3
	strout	msg_high
main3
	btfss	new_value			;new valus captured?
	goto		main4			;no
	bcf		new_value			;clear it
	movf		const0,w			;load constant for division...
	movwf	divid0
	movf		const1,w
	movwf	divid1
	movf		const2,w
	movwf	divid2
	movf		const3,w
	movwf	divid3
	movf		capture_l,w		;load capture to divisor
	movwf	divis0
	movf		capture_h,w
	movwf	divis1
	call		div32by16			;divide
	
	;now calculate average of last 3 + current value
	call		calculate_average

	movf		g0,w			;prepare conversion to BCD (average in g0:2
	movwf	f0
	movf		g1,w
	movwf	f1
	movf		g2,w
	movwf	f2
	call		bin2bcd			;convert it
	
	movf		digit5,w			;>9999? (digit 5 in use?)
	skpz						;no, then use ones
	goto		main_10er			;else use 10th
	
main_1er
    movlw   high numbers		;send to display
    movwf   PCLATH			
    movf		digit4,w
    call    numbers
    movwf   disp1
    movf		digit3,w
    call    numbers
    movwf   disp2
    movf		digit2,w
    call    numbers
    movwf   disp3
    movf		digit1,w
    call    numbers
    movwf   disp4
	movlw   high $
    movwf   PCLATH
	goto		main4
main_10er
	movf		digit1,w
	sublw	4
	btfsc	STATUS,C		;if clear, then w>=5--> need to round up
	goto		main_10er_1	;c not clear: w<5, no need to roud up
	movf		digit2,w		;now round up...add carry on ones
	addlw	0x01
	xorlw	0x0A			;overflow to 10?
	btfsc	STATUS,Z
	bsf		STATUS,C		;yes... then remember this
	xorlw	0x0A
	btfsc	STATUS,C		;if overflow than set digit to 0
	movlw	0x00
	movwf	digit2
	
	btfss	STATUS,C		;if set then we need to add 1 to next digit
	goto		main_10er_1	;else, if clear: nothing more to add
	bcf		STATUS,C
	movf		digit3,w		;add carry on tens
	addlw	0x01
	xorlw	0x0A
	btfsc	STATUS,Z
	bsf		STATUS,C
	xorlw	0x0A
	btfsc	STATUS,C
	movlw	0x00
	movwf	digit3
	
	btfss	STATUS,C		;if set then we need to add 1 to next digit
	goto		main_10er_1	;else, if clear: nothing more to add
	bcf		STATUS,C
	movf		digit4,w		;add carry on hundreds
	addlw	0x01
	xorlw	0x0A
	btfsc	STATUS,Z
	bsf		STATUS,C
	xorlw	0x0A
	btfsc	STATUS,C
	movlw	0x00
	movwf	digit4
	
	btfss	STATUS,C		;if set then we need to add 1 to next digit
	goto		main_10er_1	;else, if clear: nothing more to add
	bcf		STATUS,C
	movf		digit5,w		;add carry on thousands
	addlw	0x01
	xorlw	0x0A
	btfsc	STATUS,Z
	bsf		STATUS,C
	xorlw	0x0A
	btfsc	STATUS,C
	movlw	0x00
	movwf	digit5
	
main_10er_1
    movlw   high numbers		;send to display
    movwf   PCLATH			
    movf		digit5,w
    call    numbers
    movwf   disp1
    movf		digit4,w
    call    numbers
    movwf   disp2
	bsf		disp2,7			;switch Point on (2nd. digit)
    movf		digit3,w
    call    numbers
    movwf   disp3
    movf		digit2,w
    call    numbers
    movwf   disp4
	movlw   high $
    movwf   PCLATH
main4
	call		Delay100ms		;to make display more stable
	call		Delay100ms

    goto    main
;------------------------------------------------------------------------
;handle_menu (only function is to change the pulses per rotation)
;------------------------------------------------------------------------
handle_menu
    strout	msg_ppu			;text and current value of ppu to display
	movlw   high numbers
    movwf   PCLATH
    movf		ppu,w
    call    numbers
    movwf   disp1
 	movlw   high $
    movwf   PCLATH
    call    waitforrelease		;wait for user to leave key
	call		Delay100ms		;debounce
hm_1
	clrf		tickcounter_h		;5 secs to handle input
hm_2
	movf		tickcounter_h,w
	xorlw	d'5'				;already 5?
	skpnz					;skip if not 5
	goto		hm_end			;otherwise end ckecking
	call		keycheck			;key pressed?   
    skpnz					;skip if key pressed
	goto		hm_2				;no key, keep checking
	movf		ppu,w			;get current ppu
	addlw	d'1'				;increase it
	xorlw	d'5'				;already 5?
	skpnz					;skip if not 5
	movlw	d'4'				;else set to 4
	xorlw	d'5'				;which results in the old value if not previously 5, else results in 1 (previously 4)
	movwf	ppu				;save ppu
	movlw   high numbers		;display
    movwf   PCLATH
    movf		ppu,w
    call    numbers
    movwf   disp1
 	movlw   high $
    movwf   PCLATH
	bsf		config_changed	; we have a new config
	call		waitforrelease	;wait for key release
	call		Delay100ms		;debounce
	goto		hm_1				;and new 5 secs
hm_end	
	return					;then leave menu
;--------------------------------------------------------------------
;calculate average
;last 3 values and current one
;saves values to old0 ... old2 afterwards for next calculation
;--------------------------------------------------------------------
calculate_average
	movf		divid0,w			;add current value
	movwf	f0
	movf		divid1,w
	movwf	f1
	movf		divid2,w
	movwf	f2
	movf		old00,w			;add to old0
	movwf	g0
	movf		old01,w
	movwf	g1
	movf		old02,w
	movwf	g2
	call		add24
	movf		old10,w			;add to old1
	movwf	f0
	movf		old11,w
	movwf	f1
	movf		old12,w
	movwf	f2
	call		add24
	movf		old20,w			;add to old2
	movwf	f0
	movf		old21,w
	movwf	f1
	movf		old22,w
	movwf	f2
	call		add24
	
	bcf		STATUS,C			;divide by 4
	rrf		g2,f	
	rrf		g1,f
	rrf		g0,f
	bcf		STATUS,C			
	rrf		g2,f	
	rrf		g1,f
	rrf		g0,f
	
	movf		old10,w				;save old values to new position -3
	movwf	old00
	movf		old11,w
	movwf	old01
	movf		old12,w
	movwf	old02
	movf		old20,w				;save old values to new position -2
	movwf	old10
	movf		old21,w
	movwf	old11
	movf		old22,w
	movwf	old12
	movf		divid0,w				;save old values to new position -1
	movwf	old20
	movf		divid1,w
	movwf	old21
	movf		divid2,w
	movwf	old22
	
	return
;--------------------------------------------------------------------
;Math routines
;--------------------------------------------------------------------
;compare unsigned 16
;x=y  --> Z=1
;y<x  --> C=0
;x<=y --> C=1
;--------------------------------------------------------------------
compare_unsigned_16
    movf    xhi,w
    subwf   yhi,w           ;y-x
    skpz                    ;are they equal?
    return                  ;no, then ready
    ;otherwise compare low
    movf    xlo,w
    subwf   ylo,w           ;y-x
    return
;--------------------------------------------------------------------
;add24
;f0:2 + g0:2
;result in g0:2
;--------------------------------------------------------------------
add24
	movfw	f0              ;
    addwf	g0,F          ;LS byte

    movfw	f1             ;middle byte
    skpnc
    incfsz	f1,W
    addwf	g1,F          ;

    movfw	f2             ;MS byte
    skpnc
    incfsz	f2,W
    addwf	g2,F          ;
	return
;--------------------------------------------------------------------
;divide 32 bits by 16 by Peter Hemsley
;dividend: dividend0...dividend3
;divisor: divisor0..divisor1
;--------------------------------------------------------------------
div32by16
	movlw	d'32'		; 32-bit divide by 16-bit
    movwf	bitcnt
    clrf		remdrH		; Clear remainder
    clrf		remdrL

dvloop 
	clrc					; Set quotient bit to 0
						; Shift left dividend and quotient
    rlf		divid0		; lsb
    rlf		divid1
    rlf		divid2
    rlf		divid3		; lsb into carry
    rlf		remdrL		; and then into partial remainder
    rlf		remdrH

    skpnc				; Check for overflow
    goto		subd
    movfw	divis1		; Compare partial remainder and divisor
    subwf	remdrH,w
    skpz 
    goto		testgt		; Not equal so test if remdrH is greater
    movfw	divis0		; High bytes are equal, compare low bytes
    subwf	remdrL,w
testgt 
	skpc					; Carry set if remdr >= divis
    goto		remrlt

subd   
	movfw	divis0			; Subtract divisor from partial remainder
    subwf	remdrL
    skpc					; Test for borrow

    decf		remdrH			; Subtract borrow
    movfw	divis1
    subwf	remdrH
    bsf		divid0,0			; Set quotient bit to 1
						; Quotient replaces dividend which is lost
remrlt 
	decfsz	bitcnt
    goto		dvloop
    return
;--------------------------------------------------------------------
;Conversion routines
;--------------------------------------------------------------------
;--------------------------------------------------------------------
; Binary to BCD 
; input:f0,f1,f2
; output:digit1..digit8
; From Peter Hemsley
;--------------------------------------------------------------------
bin2bcd
	clrf		digit1
	clrf		digit2
	clrf		digit3
	clrf		digit4
	clrf		digit5
	clrf		digit6
	clrf		digit7
	clrf		digit8
	movlw	d'24'		;24 bits to do
	movwf	bitcnt

bitlp	
	rlf		f0		;Shift msb into carry
	rlf		f1
	rlf		f2

	movlw	digit1
	movwf	FSR		;Pointer to digits
	movlw	d'8'		;8 digits to do
	movwf	digcnt
adjlp	
	rlf		INDF		;Shift digit 1 bit left
	movlw	-d'10'
	addwf	INDF,w		;Check and adjust for decimal overflow
	skpnc
	movwf	INDF

	incf		FSR		;Next digit
	decfsz	digcnt
	goto		adjlp
	decfsz	bitcnt		;Next bit
	goto		bitlp
	return
;---------------------------------------------------------------
;load Prefs
;loads Preferences from internal eeprom if valid config is saved
;in eeprom. Set to default values otherwise
;---------------------------------------------------------------
load_prefs
	clrf	eprom_adr		;read pos 0 =valid-check
	call	eprom_read
	xorlw	0x55
	skpz					;go on if config is valid
	goto	load_prefs_def	;set to defaults otherwise
	;config is valid, now load them
	call		read_it
	movwf	ppu
	goto		load_prefs_end

load_prefs_def			;set default to prefs
    movlw   0x01			;1 pulse per rotation
    movwf   ppu

	goto	load_prefs_end
read_it
	incf	eprom_adr,f		;next
	call	eprom_read
	return
load_prefs_end
	return
;---------------------------------------------------------------
;save_Prefs
; save Preferences to internal eeprom
;---------------------------------------------------------------
save_prefs
	clrf		eprom_adr		;begin saving at pos 0
	movlw	0x55			;valid config marker
	movwf	eprom_dat
	call		eprom_write

	movf		ppu,w
	call		save_it

	goto		save_prefs_end
save_it
	movwf	eprom_dat
	incf		eprom_adr,f
	call		eprom_write
	return
save_prefs_end
	return
;---------------------------------------------------------------
;EEPROM handling routines
;---------------------------------------------------------------
;eprom read: Address in eprom_adr, return value in w
;---------------------------------------------------------------
eprom_read
	bcf		PIR2, EEIF		;clear interrupt flag (necessary?)
	movf		eprom_adr,w		;Set adress of read
	banksel	EEADR
	movwf	EEADR

	banksel	EECON1
	bcf		EECON1,EEPGD	;we want to read data mem
	bsf		EECON1,RD		;init read
	banksel	EEDATA
	movf		EEDATA,w
	bcf		STATUS,RP0		;back to bank 0 (our standard)
	bcf		STATUS,RP1
	return
;-------------------------------------------------------------------------
;eprom write Address in eprom_adr, data in eprom_dat returns when finished
;-------------------------------------------------------------------------
eprom_write
	bcf		PIR2, EEIF		;clear interrupt (to be sure)
	movf		eprom_adr,w		;load Adress
	banksel	EEADR
	movwf	EEADR
	banksel	0x00
	movf		eprom_dat,w	;load value
	banksel	EEDATA
	movwf	EEDATA
	banksel	EECON1
	bcf		EECON1,EEPGD	;we want to write data mem
	bsf		EECON1,WREN		;enable write

	bcf		INTCON,GIE		;we don't want interrupts now
	btfsc	INTCON,GIE		;make sure it is really cleared
	goto		$-2
	movlw	0x55			;see datasheet, required sequence
	movwf	EECON2
	movlw	0xaa
	movwf	EECON2
	bsf		EECON1,WR		;set write bit to begin write
	bsf		INTCON,GIE		;re-enable global interrupts

	banksel	0x00			;back to Bank0 (our default)
	btfss	PIR2,EEIF		;wait for flag to be set (polling)
	goto	$-1
	bcf		PIR2,EEIF		;clear it, write complete

	banksel	EECON1
	bcf		EECON1,WREN		;clear write enable
	banksel	0x00

	return
;---------------------------------------------------------------
;key handling
;returns status of keys in w
;0= nothing pressed
;8= key pressed
;---------------------------------------------------------------
;doesn't wait for anything, just checks current status
keycheck
	movf		PORTA,w
    andlw   b'00001000'     ;mask bit 3
    xorlw   b'00001000'     ;inverse
    movwf   keydata
	return
;waits for key to be pressed
getkey
	call 	keycheck
	skpnz						;if no key pressed
	goto 	getkey				;check again
	return						;otherwise return to caller
;waits for release of any key
waitforrelease
	call 	Delay10ms			;debounce
	call		keycheck
	movf		keydata,w
	skpz						;if zero then enough
	goto		waitforrelease
	call 	Delay10ms			;debounce
	return
;--------------------------------------------------------------------
;sets correct dividend for given ppu
;--------------------------------------------------------------------
set_const
	movf		ppu,w		;get current ppu
	xorlw	d'1'			;check if ppu is 1
	skpz
	goto		sd_1
	movlw	0xA0
	movwf	const0
	movlw	0x52
	movwf	const1
	movlw	0x57
	movwf	const2
	movlw	0x01
	movwf	const3
	goto		sd_end
sd_1
	xorlw	d'1'^d'2'			;check if ppu is 2
	skpz
	goto		sd_2
	movlw	0x50
	movwf	const0
	movlw	0xA9
	movwf	const1
	movlw	0xAB
	movwf	const2
	movlw	0x00
	movwf	const3
	goto		sd_end
sd_2
	xorlw	d'2'^d'3'			;check if ppu is 3
	skpz
	goto		sd_3
	movlw	0xE0
	movwf	const0
	movlw	0x70
	movwf	const1
	movlw	0x72
	movwf	const2
	movlw	0x00
	movwf	const3
	goto		sd_end
sd_3								;we get here if ppu is 4
	movlw	0xA8
	movwf	const0
	movlw	0xD4
	movwf	const1
	movlw	0x55
	movwf	const2
	movlw	0x00
	movwf	const3
sd_end
	return
;--------------------------------------------------------------------
;Delay-Subroutines (for fosc=12 MHz!!!)
;--------------------------------------------------------------------
Delay10ms					;Delays ca.10ms
	movlw	0x6F
	movwf	d1
	movlw	0x18
	movwf	d2
Delay10ms_1
	decfsz	d1, f
	goto	$+2
	decfsz	d2, f
	goto	Delay10ms_1
	goto	$+1
	return
;-----
Delay100ms					;Delays ca.100ms
	movlw	0x5F
	movwf	d1
	movlw	0xEB
	movwf	d2
Delay100ms_1
	decfsz	d1, f
	goto	$+2
	decfsz	d2, f
	goto		Delay100ms_1
	goto	$+1
	return
;########################################################################
	org	0x800	;define next in memory Page 1 (so we have space in Page 0)
;########################################################################
;---------------------------------------------------------
;Definitions for Display
;Table is short enough to ensure that no rollover from 0xff to 0x00 happens
;within this table - otherwise the code above has to be modified

numbers
    addwf   PCL,f
    dt  0x7e,0x60,0x5d,0x79,0x63,0x3b,0x3f,0x68,0x7f,0x7b
msg_low
	dt  0x00,0x00,0x16,0x7e
msg_high
	dt  0x00,0x00,0x67,0x60
msg_ppu
	dt  0x00,0x4f,0x4f,0x76
	


    end
