;************************************************************************
; Following the example of Bob Blick's original propeler clock I made 	*
; my own version of this fancy clock and have re-written the code from	*
; scratch. I used a bit more powerfull version of PIC cpu and added 	*
; these features:							*		
;  - the clock keeps time AND DATE			           	*
;  - can display analog hands or digital time and mixed mode where	*
;    time is displayed as analog hands and digital date			*
;  - setting the time/date or switch mode is done by RC5 remote control	*
;  - possibility to display a message programmed in ROM or EEPROM	*		
;                                                                 	*
;************************************************************************
;                                                               	*
;    Filename:	    prop.asm                                        	*
;    StartDate:     02/12/2001                                        	*
;    LastUpdate:    09/02/2003						*
;    File Version:  1.06                                       	   	*
;                                                                     	*
;    Author:        Soubry Henk                                       	*
;    Company:       Soubry Software Service                           	*
;                                                                     	*	 
;                                                                     	*
;************************************************************************
;                                                                    	*
;    Files required: CharGen.asm                                      	*
;                    Keys.asm  	                                      	*
;                                                                     	*
;                                                                     	*
;************************************************************************
;                                                                     	*
;    Notes:                                                           	*
;    	Pin assignment                                                	*
;      		Port A                                             	*
;		0 = inner display led (because RB0 is index)		*						
;               1 = analog outer led				   	*	                     
;               2 = Ir receiver                                       	*
;               3 = OSC Calibration output                            	*
;               4 = unused	                  		       	*
;      		Port B 							*
;		0 = index                                     	       	*
;               1..6 = Display led's (1=inner, 6=outer                	*
;               7 = analog inner led's  				*
;                                                                     	*
;************************************************************************


	list	p=16f628a            ; list directive to define processor
	#include <p16f628a.inc>        ; processor specific variable definitions
	#include "CharGen.asm"
	#include "Keys.asm"
	
	__CONFIG _CP_OFF & _WDT_OFF & _BODEN_ON & _PWRTE_ON & _HS_OSC & _MCLRE_OFF & _LVP_OFF


;***** VARIABLE DEFINITIONS

 #define	MotorCounterClockWise	; un-comment this line if motor is running counter clockwise
; #define 	ReversTextScroll	; un-comment this line if your text must scroll from left to right (e.g. Hebrew)

;Display memory
DispI		EQU     0x20	  ; 40 bytes from 0x20 -> 0x48
DispII		EQU     0xA0	  ; 80 bytes from 0xA0 -> 0xF0

;Vars in shared memory. This memory is available from any bank
  cblock	0x77
	Scratch		; memory locations for general use
	Scratch2	;
	Scratch3	;
	Scratch4	;					
	w_temp		; variable used for context saving 
	status_temp	; variable used for context saving
	fsr_temp	; variable used for context saving
	flags		; all sorts of flags 
	flags2		; 
  endc
	
;Vars for display timing routines and interrupts
  cblock	0x48
	PeriodCnt_H	; 16 bit counter counts number of interrupt
	PeriodCnt_L	; between index-puls (one revolution)
	StorePCnt1_H	; first 16 bit storage for PeriodeCounter
	StorePCnt1_L	;
	StorePCnt2_H	; second 16 bit storage for PeriodeCounter
	StorePCnt2_L	; 
	SubSec_H	; 16 bit sub-second counter
	SubSec_L	; there are 2500 sub-seconds in 1/2 second

	PixelWidth	; Pixel-width = number of interrupts in one ON pixel
	PixelOff	; 8 bit down-counter to time PixelWidth
			
	PixelPitch_H	; PixelPitch = 16 bit number of interrupts between two pixel
	PixelPitch_L	; 
	PixelPitch_F	; Fraction of PixelPitch needed for accuracy and to avoid jitter

	NewPPitch_H	; New pixel pitch calculated by main programm, will be
	NewPPitch_L	; copied to PixelPithc by interrupt routine
	NewPPitch_F	; 

	NextPixel_H	; Next pixel @ PeriodCnt = NextPixel
	NextPixel_L	;
	NextPixel_F	;
	RoundedNext_L	; RoundedNext = Round(NextPixel)
	RoundedNext_H	;
		
	iFSR		; copy of FSR used by interrupt routine
	DispOffset	; Display offset compared to indexpuls

	TmrScroll	; Count Down timer for scroll delay
	DemoCnt		; Counter for demo mode

;Vars for time-keeping			
	Hour		; looks obvious, doesn't it !
	Minute		;
	Second2		; 0..119 counting half seconds
	Day		;
	Month		;
	Year		;
	DMon		; days in current Month		
			
;Vars for building display-content
	timeticks	; index for 5min time tick
	BCD		; Binary coded decimal
	digitindex	; index digit to display
	dotindex	; index dot to display
	dpi		; 0..119 display index for main program
	tdd		; temporary display data
	PrevIndex	; stores state of index

;Vars for RC5 decoding		
	RC5_flags	;
	RC5_Tmr		;
	RC5_BitCnt	;
	RC5_Addr	;
	RC5_Cmd		;
	RC5_Cmd2	; storage for previous cmd
			
  endc

	IF RC5_Cmd2 >= Scratch
		ERROR "To many variables used"
	ENDIF

; start a new block of vars in bank2
; these are vars for the char printing routines
  cblock	0x120
	ch_dot_point_H	; pointer the the current dot# in a character	
	ch_dot_point_L	; points the the chargenerator table
	ch_dot_index	; index of dot# in a char, 0..5
	ch_num		; char number
	ch_blanking	; counter for blanking the display when scrolling text
  endc

;**********************************************************************
; give meaningfull names to the scratch locations

; for the 16bit/8bit divide routine
DIV_HI		EQU	Scratch	; 16 bit Divisor
DIV_LO		EQU	Scratch2; needed for 16 bit / 8 bit division
DIV_Q		EQU	Scratch3; 
DIVISOR		EQU	Scratch4; Divisor

;**********************************************************************
; define Port bits
bIndexPuls	EQU	0
bOuterLED	EQU	1
bRC5inp		EQU	2
bCalibration	EQU	3

; define flag bits
bDigitStart	EQU	0
bNewPCnt	EQU	1
bNewTime	EQU	2
bNewPPitch	EQU	3
bShowHand	EQU	4
bShowDTime	EQU	5
bShowDDate	EQU	6
bShowTicks	EQU	7

; define flag2 bits
bText		EQU	4
bScrollOn	EQU	5
bDemo		EQU	6

;**********************************************************************

#define 	IndexPuls	PORTB,bIndexPuls
#define 	OuterLED	PORTA,bOuterLED
#define 	RC5inp		PORTA,bRC5inp
#define		Calibration	PORTA,bCalibration

#define		digitstart	flags,bDigitStart
#define		NewPCnt		flags,bNewPCnt
#define		NewTime		flags,bNewTime
#define		NewPPitch	flags,bNewPPitch
#define		ShowHand	flags,bShowHand
#define		ShowDTime	flags,bShowDTime
#define		ShowDDate	flags,bShowDDate
#define		ShowTicks	flags,bShowTicks

#define 	fOuterLED	flags2,bOuterLED
#define		fText		flags2,bText
#define		fScrollOn	flags2,bScrollOn
#define		fDemo		flags2,bDemo

#define		RC5_WaitStart	RC5_flags,0
#define		RC5_DataReady	RC5_flags,1
#define		RC5_prev_inp	RC5_flags,2
#define		RC5_Idle	RC5_flags,3
#define		RC5_HalfBit	RC5_flags,4
#define		RC5_ReSynced	RC5_flags,5
#define		PStorage	RC5_flags,6
#define		reserved	RC5_flags,7


;******************************************************************************
;	Define macro's to Save w and Status register during interrupt
;******************************************************************************
push	macro
	movwf	w_temp
	swapf	w_temp, f
	swapf	STATUS, w
	movwf	status_temp
	endm
;
;******************************************************************************
;	Define macro's to Reload w and Status register after interrupt
;******************************************************************************
pop	macro
	swapf	status_temp, w
	movwf	STATUS
	swapf	w_temp, w
	endm
;
;******************************************************************************
;	Define macro's for a Select - Case 
;******************************************************************************
select_w	macro 
sel_last	set 0                     ;setup variable only 
         	endm 
case     	macro   val
	        xorlw   val ^ sel_last 
		btfsc	STATUS,Z
sel_last set val 
         	endm 

;
;******************************************************************************
;	Define EEPROM content for initial message
;******************************************************************************

	ORG     0x2100           ; Start off EEPROM
	de	"  ===> This version of Bob Blick's Propeller Clock was build and programmed by Henk Soubry  ",0x83,0x84,"<===              "
	de	0x00


;**********************************************************************
		ORG     0x000           ; processor reset vector
		goto    main            ; go to beginning of program

		ORG     0x004           ; interrupt vector location		
		push			; context switching		
		bcf	STATUS,RP0	; select bank 0 for interrupt stuff
		bcf	STATUS,RP1	; the 'pop' will restore current bank status
		
;**********************************************************************
		
INT_RB0		btfss	INTCON,INTF	; interrupt on RB0?
		goto	INT_TMR0	; nope, TMR0 is next

;----------	RB0 interrupt ! == Index Sensor
		movlw	0xff		;
		movwf	TMR0		; force TMR0 to 0xFF

		btfss	PStorage	;
		goto	Store2		; goto second storage
		movf	PeriodCnt_H,w	; 
		movwf	StorePCnt1_H	; StorePCnt = PeriodCnt
		movf	PeriodCnt_L,w	;
		movwf	StorePCnt1_L	;
		bcf	PStorage	;
		goto	StoreDone	;
Store2
		movf	PeriodCnt_H,w	; 
		movwf	StorePCnt2_H	; StorePCnt = PeriodCnt
		movf	PeriodCnt_L,w	;
		movwf	StorePCnt2_L	;
		bsf	PStorage	;
StoreDone:	
		bsf	NewPCnt		; signal new stored PeriodCount to main program
		clrf	PeriodCnt_H	; PeriodCnt = 0
		clrf	PeriodCnt_L	;
		clrf	NextPixel_H	; Next_Pixel = 0 (thus now)
		clrf	NextPixel_L	;
		clrf	NextPixel_F	;
		clrf	RoundedNext_L	; RoundedNext = 0
		clrf	RoundedNext_H	;
		clrf	PixelOff	; display next pixel no matter what.
		movf 	DispOffset,w	; iDispIndex = DispOffset
		movwf	iFSR		;
		btfss	NewPPitch	; is there a new value calculated by main prg?
		goto	lINT_RB0	; no, continue
		movf	NewPPitch_H,w	; PixelPitch = NewPPitch
		movwf	PixelPitch_H	; 
		movf	NewPPitch_L,w	; 
		movwf	PixelPitch_L	; 
		movf	NewPPitch_F,w	; 
		movwf	PixelPitch_F	; 
		bcf	NewPPitch	;
lINT_RB0
		bcf	INTCON,INTF	; clear RB0 interrupt flag		
		goto	lDisp_1		; do display routine but skip Period increment
;--------
INT_TMR0	btfss	INTCON,T0IF	; Test if a TMR0 interrupt occured
		goto	INT_TMR2	; nope, TMR2 is next
		
		bsf	TMR0,7		; TMR0 = TMR0 + 128 => Test to double TMR0 speed

		incfsz	PeriodCnt_L,f	; Increment 16-bit period counter
		goto	lDisp_1		;
		incfsz	PeriodCnt_H,f	;
		goto	lDisp_1		; if overflow in MSB period counter = speed to low
		bsf	STATUS,RP0	; Bank1
		movlw	0xFF		; disable LED outputs
		movwf	TRISB		;
		movlw	b'11110111'	; leave PORTA.3 as output
		movwf	TRISA		;
		bcf 	STATUS,RP0	; Bank0

lDisp_1		movf	RoundedNext_L,w	; PeriodCnt = RoundedNextPixel ?
		xorwf	PeriodCnt_L,w	;
		btfss	STATUS,Z	;
		goto	lPixelOn	; no, check if there is a pixel on
		movf	RoundedNext_H,w	;
		xorwf	PeriodCnt_H,w	;
		btfss	STATUS,Z	;
		goto	lPixelOn	; no,  check if there is a pixel on
					; yes, display next pixeldata
		movf	FSR,w		; context saving
		movwf	fsr_temp	;
		
		movf	iFSR,w		; load new memory pointer in w
		movwf	FSR		; load File Select register with Display Index
		movf	INDF,w		; PortB = DisplayData(index)
		movwf	PORTB		; 
		bcf	PORTA, 0	; dispatch bit 0 to PORTA
		btfsc	INDF, 0
		bsf 	PORTA, 0	
		
#ifdef MotorCounterClockWise
		decf	iFSR,w		; increment i(nterrupt)FSR
		call	CheckDecrement	; check correct progress of diplay memory pointer
#else
		incf	iFSR,w		; increment i(nterrupt)FSR
		call	CheckIncrement	; check correct progress of diplay memory pointer
#endif
		movwf	iFSR		;				

		movf	fsr_temp,w	; context recall
		movwf	FSR		;

		movf	PixelPitch_F,w 	; NextPixel = NextPixel + PixelPitch
		addwf	NextPixel_F,f	;
		btfss	STATUS,C	;
		goto	no_overflow	;
		incf	NextPixel_L,f	; fraction overflow, add 1 to Low byte
		btfsc	STATUS,Z	;
		incf	NextPixel_H,f	; low byte roll-over, add 1 to High byte
no_overflow:	movf	PixelPitch_L,w	;
		addwf	NextPixel_L,f	;
		btfsc	STATUS,C	;
		incf	NextPixel_H,f	; low byte overflow, add 1 to High byte
		
		movf	NextPixel_L,w	; RoundedNext = NextPixel
		movwf	RoundedNext_L	;
		movf	NextPixel_H,w	;
		movwf	RoundedNext_H	;
		btfss	NextPixel_F,7	; IF NextPixel_Fraction >= 128 then
		goto	NotRoundUp		;		
		incf	RoundedNext_L,f	; ROUND UP
		btfsc	STATUS,Z	; 
		incf	RoundedNext_H,f	;
NotRoundUp:
		movf	PixelWidth,w	;
		movwf	PixelOff	; PixelOff = PixelWidth	

		goto	lDispDone	;	

lPixelOn	movf	PixelOff,w	; Is there a pixel on?
		btfsc	STATUS,Z	; PixelOff = 0 ?
		goto	lDispDone	; no, jump

		decfsz	PixelOff,f	; pixel is on, countdown
		goto	lDispDone	;
		movlw	0x00
		movwf	PORTB		; turn off display LED's
		bcf	PORTA,0		; also turn of PortA LED
	
lDispDone		
		bcf	INTCON,T0IF	; clear TMR0 interrupt flag before return

;--------
INT_TMR2	btfss	PIR1,TMR2IF	; interrupt on TMR2?
		goto	INT_EXIT	; nope, interrupt is done!		
				
		; do the TMR2 stuff, we get here every 200uSec

		; toggle PORTA.3, use scope or freq counter to calibrate to 2.5 kHz
		movf	PORTA,w		;
		xorlw   1 << bCalibration	;
		andlw	1 << bCalibration	;
		bcf	Calibration	; Toggle PORTA.3
		btfss	STATUS,Z	; 
		bsf	Calibration	;

		; Text-scroll timer
		movf	TmrScroll,f	; TmrScroll == 0 ?
		btfss	STATUS,Z	;
		decf	TmrScroll,f	; no, TmrScroll --

		; real time sub-second counter
		incfsz	SubSec_L,f	; Increment 16-bit Sub Second counter
		goto	lTime_1		; stil counting
		incfsz	SubSec_H,f	;
		goto	lTime_1		; stil counting
		
		incf	Second2,f	; 1/2 second has passed
		bsf	NewTime		; signal new time to main program
		movlw	0x3C		; reload counter SubSecond = 0x10000 - .2500 = 0xF63C
		movwf	SubSec_L	;
		movlw	0xF6		;
		movwf	SubSec_H	;
		
lTime_1		; start RC5 stuff here
		btfsc	RC5_DataReady	;
		goto	lRC5_Exit	;
		btfss	RC5_Idle	;
		goto	lRC5_Not_Idle	;
		decfsz	RC5_Tmr	,f	;
		goto	lRC5_Exit	;		
		btfsc	RC5inp		; test input
		bcf	RC5_Idle	; input = high, cancel Idle state
		incf	RC5_Tmr,f	; continue Idle state until input = high
		goto	lRC5_Exit	;		
lRC5_Not_Idle	
		btfss	RC5_WaitStart	;
		goto	lRC5_ReSync	;
lRC5WaitStart	btfsc	RC5inp		; test input
		goto	lRC5_Exit	; no startbit
		bcf	RC5_WaitStart	; start received
		movlw	.6		;
		movwf	RC5_Tmr		;
		movlw	.13		; 13 bits to receive
		movwf	RC5_BitCnt	;
		clrf 	RC5_Addr	;
		clrf	RC5_Cmd		;
		goto	lRC5_Exit	;
lRC5_ReSync				;
		movf	PORTA,w		;
		xorwf	RC5_flags,w	;
		andlw	0x04		;
		btfsc	STATUS,Z	;
		goto	lRC5_no_sync	;
		bsf	RC5_ReSynced	;
		movlw	.6		; re-sync the timer
		btfss	RC5_HalfBit	;
		movlw	.2		;
		movwf	RC5_Tmr		;
		bcf	RC5_prev_inp	; clear previous input
		btfsc	RC5inp		;
		bsf	RC5_prev_inp	;
		
lRC5_no_sync	btfsc	RC5_HalfBit	;
		goto	lRC5_2nd_Half	;

lRC5_1st_Half	decfsz	RC5_Tmr,f	;
		goto	lRC5_Exit	;	
		bcf	STATUS,C	;
		btfsc	RC5inp		;
		bsf	STATUS,C	; C = RC5inp
		rlf	RC5_Cmd,f	;
		rlf	RC5_Addr,f	; 
		bsf	RC5_HalfBit	; indicate that the first half bit is received
		bcf	RC5_ReSynced	;
		movlw	.4		;
		movwf	RC5_Tmr		; reload timer 
		goto	lRC5_Exit
lRC5_2nd_Half   
		btfsc	RC5_ReSynced	;
		goto	lReSyncOK	;
		decfsz	RC5_Tmr,f	;
		goto	lRC5_Exit	; 
lRC5_Error	clrf	RC5_flags	;
		bsf	RC5_WaitStart	;
		bsf	RC5_Idle	;
		movlw	.128		;
		movwf	RC5_Tmr		;
		goto	lRC5_Exit	;		
lReSyncOK	
		; test second bit half
		bcf	RC5_HalfBit	;
		decfsz	RC5_BitCnt,f	;
		goto	lRC5_Exit	;
		rlf	RC5_Cmd,f	; Shift bit right to compleet Addr
		rlf	RC5_Addr,f	;
		rlf	RC5_Cmd,f	;
		rlf	RC5_Addr,f	;
		bcf	STATUS,C	;
		btfss	RC5_Addr,6	;
		bsf	STATUS,C	; C = Inv Bit6 in RC5_Addr
		rrf	RC5_Cmd,f	;
		bcf	STATUS,C	;
		btfsc	RC5_Addr,5	;
		bsf	STATUS,C	; C = ToggleBit in RC5_Addr
		rrf	RC5_Cmd,f	;
		movlw	0x1F		;
		andwf	RC5_Addr,f	;
		bsf	RC5_DataReady	;
lRC5_Exit			
		bcf	PIR1,TMR2IF	; clear TMR2 interrupt flag
;--------
INT_EXIT		
		pop			; restore conext for main program
					; this will also restore bank selection
		retfie                  ; return from interrupt

;**********************************************************************

div16X8		; DIV_HI and DIV_LO / DIVISOR.  result to DIV_Q
		; remainder in DIV_LO
		; does not deal with divide by 0 case

		clrf 	DIV_Q
div_1
		movf 	DIVISOR, W
		subwf 	DIV_LO, F
		btfss 	STATUS, C	; if positive skip
		goto 	div_borrow
		goto 	div_2
div_borrow
		movlw 	.1
		subwf 	DIV_HI, F	; DIV_HI = DIV_HI - 1
		btfss 	STATUS, C	; if no borrow occurred
		goto	div_done	
div_2
		incf DIV_Q, F
		goto div_1
div_done
		movf DIVISOR, W		; re-add DIVISOR to DIV_LO to get
		addwf DIV_LO, F		; remainder in DIV_LO
	
		retlw	0

CalcPixelPitch	; thats a hard one. We have to divide by 120 !!!
		; PixelPitch = PeriodCnt / 120
		
		movf 	StorePCnt1_H,w	; check if two period counters
		xorwf	StorePCnt2_H,w	; are the same. If not, don't
		btfss	STATUS,Z	; calculate new pixelpitch because
		goto	CalcDone	; the rotation speed was not stable
		movf	StorePCnt1_L,w	;
		xorwf	StorePCnt2_L,w	;
		btfss	STATUS,Z	;
		goto	CalcDone	;		

		clrf	DIV_HI		; Divisor = PeriodCount_H
		movf	StorePCnt1_H,w	;
		movwf	DIV_LO		; 
		movlw	.120
		movwf	DIVISOR
		
		call 	div16X8
		
		movf	DIV_Q,w		; store result in High byte
		movwf	NewPPitch_H	;
		movf	DIV_LO,w	; shift remainder to DIV_HI
		movwf	DIV_HI		;
		movf	StorePCnt1_L,w	;
		movwf	DIV_LO		;
		
		call	div16X8
		
		movf	DIV_Q,w		; store result in Low byte
		movwf	NewPPitch_L	;
		movf	DIV_LO,w	; shift remainder to DIV_HI
		movwf	DIV_HI		;
		clrf	DIV_LO		;
		
		call	div16X8

		movf	DIV_Q,w		; store result in Fraction byte
		movwf	NewPPitch_F	;

		; start calculation for pixel width
		clrc			; Clear Carry
		rrf	NewPPitch_L,w	;
		movwf	PixelWidth	; PixelWidth = NewPPitch_L / 2
		movwf	Scratch		;
		clrc
		rrf	Scratch,f	;
		clrc	
		rrf	Scratch,w	; w = NewPPitch / 8
		addwf	PixelWidth,f	; PixelWidth = NewPPitch * 5 / 8

		bsf	STATUS,RP0	; Bank1
		movlw	0x01		; 
		movwf	TRISB		; PortB1..7 as output, PortB0 as input
		movlw	b'11110100'	; Port A bit 0, 1 and 3 as output
		movwf	TRISA		; Port A bit 2, 3, 4 as input
		bcf	STATUS,RP0	; Bank0

		btfss	fOuterLED	;
		bcf	OuterLED	; set Port for Outerled equal to fOuterLED
		btfsc	fOuterLED	;
		bsf	OuterLED	;

NotON:
		bsf	NewPPitch	; signal new Pitch value to interrupt routine
CalcDone:
		bcf	NewPCnt		; clear flag		

		retlw 	0

;******************************************************************************
;	Check correct progress of sec,min,hour,day,month,year
;******************************************************************************

TimeCheck	; second variable is changed, check if an overflow
		; is generated to minute, hour, day,...

		movlw	.120		; 
		subwf	Second2,w	; Second2 < 120 ? (counting 120 half seconds)
		btfss	STATUS,C        ; if second2 < 120 then c=0 (borrow)
		goto	lMinute		; 
					; yes, Second >= 120
		movwf	Second2		; Second = Second - 120
		incf	Minute,f	; Minute = Minute + 1
lMinute		
		movlw	.60		; 
		subwf	Minute,w	; Minute < 60 ?
		btfss	STATUS,C        ; if Minute < 60 then c=0 (borrow)
		goto	lHour		; 
					; yes, Minute >= 60
		movwf	Minute		; Minute = Minute - 60
		incf	Hour,f		; Hour = Hour + 1
lHour
		movlw	.24		; 
		subwf	Hour,w		; Hour < 24 ?
		btfss	STATUS,C        ; if Hour < 24 then c=1 (borrow)
		goto	lDay		; 
					; yes, Hour >= 24
		movwf	Hour		; Hour = Hour - 24
		incf	Day,f		; Day = Day + 1
lDay		
		movlw	.32		; DMon = 32
		movwf	DMon		
		movf	Month,w		; 
		xorlw	.4		; test for april
		btfsc	STATUS,Z	;
		decf	DMon,f		; Dmon = Dmon - 1 
		xorlw	.6 ^ .4		; test for juni
		btfsc	STATUS,Z	;
		decf	DMon,f		; Dmon = Dmon - 1 
		xorlw	.9 ^ .6		; test for september
		btfsc	STATUS,Z	;
		decf	DMon,f		; Dmon = Dmon - 1 
		xorlw	.11 ^ .9	; test for november
		btfsc	STATUS,Z	;
		decf	DMon,f		; Dmon = Dmon - 1 
	
		xorlw	.2 ^ .11	; Month = Februari ?
		btfss	STATUS,Z	; 
		goto	lNotFeb		; continue
		movlw	.29		
		movwf	DMon		;
		btfsc	Year,0		;
		goto	lNotFeb		;
		btfsc	Year,1		;
		goto	lNotFeb		;
		incf	DMon,f		; Year is LeapYear, inc DMon for Feb			
	
lNotFeb
		movf	DMon,w		; w = DMon
		subwf	Day,w		; Day < DMon ?
		btfss	STATUS,C        ; if Day < DMon then c=1 (borrow)
		goto	lMonth		; 
		
		movwf	Day		; Day = (Day - DMon)+1
		incf	Day,f		; 
		incf	Month,f		; Month = Month + 1
		
lMonth
		movlw	.13		; 
		subwf	Month,w		; Month < 13 ?
		btfss	STATUS,C        ; if Month < 13 then c=0 (borrow)
		goto	lYear		; 
					
		movwf	Month		; Month = Month - 12
		incf	Month,f		;
		incf	Year,f		; Year = Year + 1
	
lYear		
		movlw	.100		;
		subwf	Year,w		; Year < 100 ?
		btfsc	STATUS,C	; 
		clrf	Year		;		

		bcf	NewTime		;
		retlw 	0


;******************************************************************************
;	Routines to display Time and Date
;******************************************************************************

; CHARACTER LOOKUP TABLE
; set=LED on, clear=LED off
Char_tbl1
   	addwf   PCL,f

	DT  0x3E , 0x41 , 0x41 , 0x41 , 0x3E ; "0"
;	DT  0x3E , 0x45 , 0x49 , 0x51 , 0x3E ; "0"
	DT  0x00 , 0x21 , 0x7F , 0x01 , 0x00 ; "1"
	DT  0x21 , 0x43 , 0x45 , 0x49 , 0x31 ; "2"
	DT  0x42 , 0x41 , 0x51 , 0x69 , 0x46 ; "3"
	DT  0x0C , 0x14 , 0x24 , 0x7F , 0x04 ; "4"
	DT  0x72 , 0x51 , 0x51 , 0x51 , 0x4E ; "5"
	DT  0x1E , 0x29 , 0x49 , 0x49 , 0x06 ; "6"
	DT  0x40 , 0x47 , 0x48 , 0x50 , 0x60 ; "7"
	DT  0x36 , 0x49 , 0x49 , 0x49 , 0x36 ; "8"
	DT  0x30 , 0x49 , 0x49 , 0x4A , 0x3C ; "9"
	DT  0x00 , 0x36 , 0x36 , 0x00 , 0x00 ; ":"
	DT  0x00 , 0x00 , 0x00 , 0x00 , 0x00 ; " "
Char_tbl1_end

;-flipped digits upside down and mirrored
;-needed for the bottom display
Char_tbl2
   	addwf   PCL,f
	DT 0x3E , 0x41 , 0x41 , 0x41 , 0x3E ; "0"	
	DT 0x00 , 0x40 , 0x7F , 0x42 , 0x00 ; "1"
	DT 0x46 , 0x49 , 0x51 , 0x61 , 0x42 ; "2"
	DT 0x31 , 0x4B , 0x45 , 0x41 , 0x21 ; "3"
	DT 0x10 , 0x7F , 0x12 , 0x14 , 0x18 ; "4"
	DT 0x39 , 0x45 , 0x45 , 0x45 , 0x27 ; "5"
	DT 0x30 , 0x49 , 0x49 , 0x4A , 0x3C ; "6"
	DT 0x03 , 0x05 , 0x09 , 0x71 , 0x01 ; "7"
	DT 0x36 , 0x49 , 0x49 , 0x49 , 0x36 ; "8"
	DT 0x1E , 0x29 , 0x49 , 0x49 , 0x06 ; "9"
	DT 0x00 , 0x08 , 0x08 , 0x08 , 0x00 ; "-"
	DT 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ; " "
Char_tbl2_end

	IF Char_tbl1 < 0x100
		ERROR "Tabel1 pages error"
        ENDIF

	IF Char_tbl2_end >= 0x200
		ERROR "Tabel2 pages error"
        ENDIF

;-------- Update Display memory with Hands

Hands		movlw	.30		; difference in offset for hands and digital display
		subwf	dpi,w		; scratch4 = dpi - 30
		btfss	STATUS,C	; if scratch4 < 0 then 
		addlw	.120		;    scratch4 = scartch4 + 120
		movwf	Scratch4	; 

		movf	Second2,w	;
		andlw	0xFE		; filter out 1/2 second
		xorwf	Scratch4,w	;
		btfsc	STATUS,Z	;
		bsf	tdd,5	;
		
		bcf	STATUS,C	;
		rlf	Minute,w	;
		xorwf	Scratch4,w	;
		btfss	STATUS,Z	;
		goto	lhour		;
		movlw	B'10011111'	;
		iorwf	tdd,f	;	
		
lhour		movf	Hour,w		;
		movwf	Scratch2	; 
		bcf	STATUS,C	; clear carry
		rlf	Scratch2,f	; Scratch2 * 2
		rlf	Scratch2,f	; Scratch2 * 2
		addwf	Scratch2,f	; Scratch2 = 5 * hour
		rlf	Scratch2,f	; Scratch2 = 10 * Hour
		movf	Minute,w	; 
		movwf	Scratch		; Scratch = minute
		movlw	.6		;
l_hour_div6	subwf	Scratch,f	;
		btfss	STATUS,C	;
		goto	l_div6_done	;
		incf	Scratch2,f	; advance hour abit
		goto	l_hour_div6	;
l_div6_done	movlw	.120		; Scratch2 = (10 * Hour) + (Minute / 6)
l_120		subwf	Scratch2,f	; 
		btfsc	STATUS,C	; result > 0 ?
		goto	l_120
		addwf	Scratch2,w	; result was negatif, re-add 120 and load it in w
		xorwf	Scratch4,w	;
		btfss	STATUS,Z	;
		return			;
		movlw	B'10000001'	;
		iorwf	tdd,f	;	
		return

;-------- Update Display memory with Digital Time Display

DTime		movlw	.6		;
		subwf	dpi,w		;
		btfss	STATUS,C	;
		return			; display index < 6
		btfss	STATUS,Z	;
		goto	l_DTime_1	; display index > 6

		clrf	digitindex	; display index = 6, load start parameters for
		clrf	dotindex	; digit displaying

l_DTime_1	movlw	.54		;
		subwf	dpi,w		;
		btfsc	STATUS,C	;
		return			; display index >= 54 
		
		movf	dotindex,w	;
		btfss	STATUS,Z	;
		goto	l_DTime_3
		call	LoadBCDTime	; dotindex = 0, load new digit
		movf	digitindex,w	;
		btfss	STATUS,Z	;
		goto	l_DTime_3
		movf    BCD,w		; digit index = 0, 10 hour digit
		btfss 	STATUS,Z	;
		goto	l_DTime_3	;
		movlw	0x37		; 10 hour digit is zero, remove leading zero
		movwf	BCD		; BCD = " "
l_DTime_3
		incf	dotindex,f	;
		movlw	.6		;
		xorwf	dotindex,w	;
		btfss	STATUS,Z	;
		goto	l_DTime_2	; dotindex < 6, display data
		clrf	dotindex	; dotindex = 6, reset dotindex and don't display 
		incf	digitindex,f	; select next digit
		return			; to get a gap between digits
l_DTime_2
		movf	BCD,w		;
		bcf	PCLATH,1	; set to page 0x100 for lookup
		bsf	PCLATH,0	
                call    Char_tbl1       ; get the dot pattern for this column
		movwf	tdd		;
		incf	BCD,f		;
		return			;

;-------- Update Display memory with Digital Date Display

DDate		movlw	.66		;
		subwf	dpi,w		;
		btfss	STATUS,C	;
		return			; display index < 66
		btfss	STATUS,Z	;
		goto	l_DDate_1	; display index > 66

		clrf	digitindex	; display index = 66, load start parameters for
		clrf	dotindex	; digit displaying

l_DDate_1	movlw	.114		;
		subwf	dpi,w		;
		btfsc	STATUS,C	;
		return			; display index >= 114 
		
		movf	dotindex,w	;
		btfsc	STATUS,Z	;
		call	LoadBCDDate	; dotindex = 0, load new digit

		incf	dotindex,f	;
		movlw	.6		;
		xorwf	dotindex,w	;
		btfss	STATUS,Z	;
		goto	l_DDate_2	; dotindex < 6, display data
		clrf	dotindex	; dotindex = 6, reset dotindex and don't display 
		incf	digitindex,f	; select next digit
		return			; to get a gap between digits
l_DDate_2
		movf	BCD,w		;
		bcf	PCLATH,1	; set to page 0x100 for lookup
		bsf	PCLATH,0	
                call    Char_tbl2       ; get the dot pattern for this column
		movwf	tdd		;
		incf	BCD,f		;
		return			;
;--------
Conv2BCD	movwf	Scratch		;
		clrf	Scratch2	;
		movlw	.10		;
l_bcd_1		subwf	Scratch,f	;
		btfss	STATUS,C	;
		goto	l_bcd_2
		incf	Scratch2,f	;
		goto	l_bcd_1
l_bcd_2		addwf	Scratch,f	;
		swapf	Scratch2,w	;
		addwf	Scratch,w	;
		return
;--------
LoadBCDDate	; load BCD with date digits
		movf	digitindex,w	;
		clrf	PCLATH
		bsf	PCLATH,1	; set to page 0x200 for lookup
		addwf	PCL,f		;
		goto	l_1year		;
		goto	l_10year	;
		goto	l_dot		;
		goto	l_1mon		;
		goto	l_10mon		;
		goto	l_dot		;
		goto	l_1day		;
		goto	l_10day		;

LoadBCDTime	; load BCD with time digits
		movf	digitindex,w	;
		clrf	PCLATH
		bsf	PCLATH,1	; set to page 0x200 for lookup
		addwf	PCL,f		;
		goto	l_10hour	;
		goto	l_1hour		;
		goto	l_dot		;
		goto	l_10min		;
		goto	l_1min		;
		goto	l_dot		;
		goto	l_10sec		;
;		goto	l_1sec		;

l_1sec		bcf	STATUS,C	; Second2 / 2 = Second
		rrf	Second2,w	;
		goto	l_convert	;
l_1min		movf	Minute,w
		goto	l_convert	;
l_1hour		movf	Hour,w
		goto	l_convert	;
l_1day		movf	Day,w
		goto	l_convert	;
l_1mon		movf	Month,w
		goto	l_convert	;
l_1year		movf	Year,w
l_convert	call	Conv2BCD
		andlw	0x0F		;
		movwf	BCD		;		
		goto	l_mult5		; multiply BCD by 5

l_10sec		bcf	STATUS,C	; Second2 / 2 = Second
		rrf	Second2,w	;
		goto	l_convert_swap	;
l_10min		movf	Minute,w	;
		goto	l_convert_swap	;
l_10hour	movf	Hour,w
		goto	l_convert_swap	;
l_10day		movf	Day,w
		goto	l_convert_swap	;
l_10mon		movf	Month,w
		goto	l_convert_swap	;
l_10year	movf	Year,w
l_convert_swap	call	Conv2BCD
		andlw	0xF0		;		
		movwf	BCD		;
		swapf	BCD,f		;
		movf	BCD,w		;
l_mult5		rlf	BCD,f		; BCD x 2
		rlf	BCD,f		; BCD x 2
		addwf	BCD,f		; + BCD
		return		
				
l_dot		movlw	0x32		; 0x0A * 5 = 0x32
		movwf	BCD		;		
		return
		
;-------- Update Display memory with Time Ticks every 5 minutes

Ticks		movf	dpi,w		;
		xorwf	timeticks,w	;
		btfss	STATUS,Z	;
		return
		bsf	tdd,6
		movlw	.10
		addwf	timeticks,f	; set next tick @
		return

;******************************************************************************
;	Processing of RC5 command's, called by main program if a command is received
;******************************************************************************

ProcessRC5
		movf	RC5_Addr,w		;		
		xorlw	RemoteAddr		; test if RC5_Addr = RemoteAddr
		btfss	STATUS,Z		; 
		goto	ProcessRC5Done		;

		movf	RC5_Cmd,w		;
		andlw	0x7F			;	
		
		select_w
		case	NUM_0			;
		  clrf	Second2			; Zero pressed => Seconds = 0
		case	SEC_UP			;
		  goto	IncSecond		; Adjust time : Increment Seconds
		case	SEC_DN			;
		  goto	DecSecond		; Adjust time : Decrement Seconds
		case	MIN_UP			;
		  incf	Minute,f		; Adjust time : Increment Minutes
		case	MIN_DN			;
		  goto	DecMinute		; Adjust time : Decrement Minutes
		case	HOUR_UP			;
		  goto	IncHour			; Adjust time : Increment Hours
		case	HOUR_DN			;
		  goto	DecHour			; Adjust time : Decrement Hours
		case	DAY_UP			;
		  goto	IncDay			; Adjust Date : Increment Days
		case	DAY_DN			;
		  goto	DecDay			; Adjust Date : Decrement Days
		case	MON_UP			;
		  goto	IncMonth		; Adjust Date : Increment Month
		case	MON_DN			;
		  goto	DecMonth		; Adjust Date : Decrement Month
		case	YEAR_UP			;
		  incf	Year,f			; Adjust Date : Increment Year
		case	YEAR_DN
		  goto	DecYear			; Adjust Date : Decrement Year
		case	INDEX_UP		;
		  goto	DecDispOffset		; Adjust index sensor Offset, rotate display left
		case	INDEX_DN		;
		  goto	IncDispOffset		; Adjust index sensor Offset, rotate display right
		
		; Toggle functions
		movf	RC5_Cmd,w		;
		xorwf	RC5_Cmd2,f		;
		btfsc	STATUS,Z		;
		goto	ProcessRC5Done		;
		andlw	0x7F			;	

		select_w
		case	BlueLine 		;
		  goto	ToggleOuterLed		; Blue Line
		case	DigiTime		;
		  goto	ToggleTime		; Digital Time
		case	DigiDate		;
		  goto	ToggleDate		; Digital Date
		case	AnaTime			;
		  goto	ToggleAnalog		; Analog Time
		case	TTicks			;
		  goto	ToggleTick		; Analog Time Ticks
		case	DM			;
		  goto	ToggleDemo		; Demo Mode
		case	TextMode		;
		  goto	ToggleText		; Text Mode

ProcessRC5Done		
		movf	RC5_Cmd,w	;
		movwf	RC5_Cmd2	; 
		bsf	NewTime		; force display update
		bcf	RC5_DataReady	;
		return			;
		
ToggleOuterLed	movlw	1 << bOuterLED	;
		xorwf	flags2,f	;
		bcf	OuterLED	;
		btfsc	fOuterLED	;
		bsf	OuterLED	;
		goto	ProcessRC5Done	;
		
ToggleTime	movlw	1 << bShowDTime	;
		xorwf	flags,f		;
		goto	ProcessRC5Done	;
		
ToggleDate	movlw	1 << bShowDDate	;
		xorwf	flags,f		;
		goto	ProcessRC5Done	;
		
ToggleAnalog	movlw	1 << bShowHand	;
		xorwf	flags,f		;
		goto	ProcessRC5Done	;
		
ToggleTick	movlw	1 << bShowTicks	;
		xorwf	flags,f		;
		goto	ProcessRC5Done	;
		
IncDispOffset	incf	DispOffset,w	;
		call 	CheckIncrement 	;
		movwf	DispOffset	;				
		goto	ProcessRC5Done	;

DecDispOffset	decf	DispOffset,w	;
		call 	CheckDecrement	;
		movwf	DispOffset	;				
		goto	ProcessRC5Done	;
		
IncSecond	incf	Second2,f	;
		incf	Second2,f	;
		goto	ProcessRC5Done	;
		
DecSecond	movlw	.2		;
		subwf	Second2,f	;
		btfsc	STATUS,C	;	
		goto	ProcessRC5Done	;
		movlw	.120		;
		addwf	Second2,f	;
DecMinute
		movlw	.1
		subwf	Minute,f	;
		btfsc	STATUS,C	;
		goto	ProcessRC5Done	;
		movlw	.60		;
		addwf	Minute,f	;
DecHour
		movlw	.1
		subwf	Hour,f		;
		btfsc	STATUS,C	;
		goto	ProcessRC5Done	;
		movlw	.24		;
		addwf	Hour,f		;
		goto	ProcessRC5Done	;

IncHour		incf	Hour,f		;
		movlw	.24		;
		xorwf	Hour,w		;
		btfsc	STATUS,Z	;
		clrf	Hour		;
		goto	ProcessRC5Done	;

IncDay		decf	DMon,w		;
		xorwf	Day,w		;
 	        btfsc	STATUS,Z	;
		clrf	Day		;
		incf	Day,f		;		
		goto	ProcessRC5Done	;

DecDay		decfsz	Day,f		;
		goto	ProcessRC5Done	;
		decf	DMon,w		;
		movwf	Day		;
		goto	ProcessRC5Done	;

IncMonth	movlw	.12		;
		xorwf	Month,w		;
		btfsc	STATUS,Z	;
		clrf	Month		;
		incf	Month,f		;
		goto	ProcessRC5Done	;

DecMonth	decfsz	Month,f		;
		goto	ProcessRC5Done	;
		movlw	.12		;
		movwf	Month		;
		goto	ProcessRC5Done	;

DecYear		movlw	.1		;
		subwf	Year,f		;
		btfsc	STATUS,C	;
		goto	ProcessRC5Done	;
		movlw	.99		;
		movwf	Year		;
		goto	ProcessRC5Done	;

ToggleText	call	TextON_OFF	; Toggel Text mode
		goto	ProcessRC5Done	;

ToggleDemo	movlw	1 << bDemo	;
		xorwf	flags2,f	;
		goto	ProcessRC5Done	;

TextON_OFF	bcf	fScrollOn	; Scrolling OFF
		movlw	1 << bText	; toggle Text flag
		xorwf	flags2,f	;
		btfss	fText		; test Text flag
		goto	ProcessRC5Done	;
		call	ClearDisplay	;
		bsf	fScrollOn	; Scrolling ON
		return
			
;******************************************************************************
;	Initialisation routines, called by main program
;******************************************************************************

InitRam
		clrf	PixelWidth	;
		clrf	PixelOff	;
		movlw	0x37		; Initial Display Offset for my hardware !!!
		movwf	DispOffset	;
		movwf	iFSR		;

		movlw	0x3C		; load counter SubSecond = 0x10000 - 2500 = 0xF63C
		movwf	SubSec_L	;
		movlw	0xF6		;
		movwf	SubSec_H	;
		clrf	flags		; clear all flags
		clrf	flags2		;

		movlw	.12		;why do clocks always start
		movwf	Hour		;at 12:00 ?
		clrf	Minute
                clrf	Second2
		movlw	0x01
		movwf	Day
		movwf	Month
		movlw	Year
				
		clrf	RC5_flags	; clear flags
		clrf	RC5_Tmr		;
		bsf	RC5_WaitStart	; RC5 is waiting for startbit
		return
		
;--------		
InitIO
		clrf	PORTA		; all LED's OFF
		clrf	PORTB

		bcf	STATUS,IRP	; Bank0.1 for FSR/INDF operations
		bcf	STATUS,RP1	; 
		bsf	STATUS,RP0	; Bank1
		movlw	0xFF		;
		movwf	TRISA		; set all is input
 		bcf	TRISA, bCalibration	; Set Calibration pin as output
 		movwf	TRISB		; outputs will be turned on if rotation is stable
		bcf	STATUS,RP0	; Bank0

		movlw	0x07		;
		movwf	CMCON		; comparators OFF
		
		retlw	0
;--------		
InitTmr 
		clrf	INTCON		; clear and dissable all possible interrupt flag
		clrf	PIR1		; 

		movlw	b'00000101'	; TMR2=ON, Prescaler = 1/4
		movwf	T2CON		; 

		bsf	STATUS,RP0	; Bank1
		clrf	PIE1		; dissable all possible interrupt flag
		movlw	b'10011000'	; set up timer. prescaler(bit3)bypassed, Int on falling Edge RB0
		movwf	OPTION_REG	; send w to option.
		movlw	.249		; TMR2 PR2=249
		movwf	PR2		;
		bsf	PIE1,TMR2IE	; enable Peripheral interrupt from TMR2
		bcf	STATUS,RP0	; Bank0

		bsf	INTCON,T0IE	; enable TMR0 interrupt
		bsf	INTCON,INTE	; enable RB0 interrupt
		bsf	INTCON,PEIE	; enable Peripheral interrupts
		bsf	INTCON,GIE	; enable global interrupts

		clrf	TMR0		; start timer
		return

;--------		
ClearDisplay
		movlw	0x20		; set FSR to 0x20
		movwf	FSR		;
cldisp
		clrf	INDF		; clear display memory
		incf	FSR,f		; 
		movlw	0x48
		xorwf	FSR,w		; FSR = 0x48
		btfss	STATUS,Z	; ??
		goto	cldisp_2	; nope, jump
		movlw	0x58		; w = 0x58
		addwf	FSR,f		; FSR = FSR + 0x58 = 0xA0
cldisp_2
		movlw	0xF0		; 
		xorwf	FSR,w		; FSR = 0xF0
		btfss	STATUS,Z	; ??
		goto	cldisp		; nope, jump

		; init stuff needed for Text Scroll function
		bsf	STATUS,RP1	; jump to bank 2
      		clrf	ch_dot_index	; 
      		clrf	ch_blanking	;
      		bcf	STATUS,RP1	; return to bank 0
		bsf	STATUS,RP0	; goto bank 1
		clrf	EEADR		; inc address for next read
		bcf	STATUS,RP0	; goto bank 0

		return

;******************************************************************************
;	Main program 
;******************************************************************************

main

		call InitIO		; Initialise Ports
		call InitRam		; Initialise Ram and flags
		call ClearDisplay	; clear display content
		call InitTmr		; Initialise Timers and interrupt

		bsf	ShowHand	; turn analog hands on
		bsf	ShowDTime	;
		bsf	ShowDDate	;
		bsf	ShowTicks	;
		bsf	OuterLED	;
MainLoop
		btfsc	RC5_DataReady	;
		call	ProcessRC5	;

		btfss	fScrollOn	;
		goto	NoScrolling	;
		movf	TmrScroll,w	;
		btfss	STATUS,Z	;
		goto	NoScrolling	;
		decf	TmrScroll,f	; TmrScroll-- => 0 - 1 = 0xFF 
		call	ScrollText	;

NoScrolling
		btfsc	NewPCnt		; test Period counter flag
		call	CalcPixelPitch  ; calculate new pixel pitch

		btfss	NewTime		; test new time flag
		goto	MainLoop	;

; one second past. Do timecheck and if text=off, also a display update.
		call	TimeCheck	;

		btfsc	fDemo		;
		call	DemoMode	;

		btfsc	fText		; test Image flag
		goto  	MainLoop	; if Image flag SET, leave display-content unchanged

; start display memory update
		clrf	dpi		; dpi = 0
		clrf	timeticks	; keep index for 5 min timeticks
		bcf	digitstart	; 
		movlw	0x20		; start of display memory
		movwf	FSR
		bcf	digitstart	;

lUpdateDisplay	
		
		clrf	tdd		;

		btfsc	ShowDTime	;
		call	DTime		; Display digital Time
		
		btfsc	ShowDDate	;
		call	DDate		; Display digital Date
		
		btfsc	ShowHand	;
		call	Hands	
		btfsc	ShowTicks	;
		call	Ticks		;
		
		movf	tdd,w		; store data in 
		movwf	INDF		; display memory
		
		incf	dpi,f		;
		movlw	.120		;
		xorwf	dpi,w		;
		btfsc	STATUS,Z	;
		goto  	MainLoop	;
		
		incf	FSR,f		;
		movlw	0x48		; for memory gap
		xorwf	FSR,w		; FSR = 0x48
		btfss	STATUS,Z	; ??
		goto	lUpdateDisplay	; nope, jump
		movlw	0x58		; w = 0x58
		addwf	FSR,f		; FSR = FSR + 0x58 = 0xA0		
		goto	lUpdateDisplay
		
;******************************************************************************
;	Text display functions
;******************************************************************************

CharOffset	movwf	ch_dot_point_L		; store ASC code
		clrf	ch_dot_point_H		; clear high byte
		movlw	.32			; asc code - 32 
		subwf	ch_dot_point_L,f	; 
		btfss	STATUS,C		; ASC < 32 ? Invalid code, 
		clrf	ch_dot_point_L		; relpace with CHR(32) = " "
		movlw	.127			;
		subwf	ch_dot_point_L,w	;
		btfsc	STATUS,C		; ASC >= 127 ? Invalid code,
		movwf	ch_dot_point_L		; replace with last valid ASC code
	
		bcf 	STATUS,C		; Clear Carry flag
		rlf	ch_dot_point_L,f	; asc code * 2
		rlf	ch_dot_point_H,f	; 		

		bcf 	STATUS,C		; Clear Carry flag		
		rlf	ch_dot_point_L,w	; 
		movwf	Scratch			; Scratch = (asc code * 2) *2
		rlf	ch_dot_point_H,w	;
		movwf	Scratch2		; 
		
		movf	Scratch,w		; ch_dot_point (16bit) = (asc code - 32) * 6
		addwf	ch_dot_point_L,f	; 
		btfsc	STATUS,C		; 
		incf	Scratch2,f		;
		movf	Scratch2,w		;
		addwf	ch_dot_point_H,f	; ch_dot_point = ch_dot_point + 0x500 start of char gen table
		movlw	0x05			; this is the entry in the char gen table for the
		addwf	ch_dot_point_H,f	; first pixel data of the requested char
		
		movlw	.6
		movwf	ch_dot_index		; indicate start of new char
		return

;--------		
LoadChrData	movf	ch_dot_point_H,w	;
		movwf	PCLATH			;
		movf	ch_dot_point_L,w	;
		call	CharGen			;
		incf	ch_dot_point_L,f	; pointer++
		btfsc	STATUS,Z		;
		incf	ch_dot_point_H,f	;
		decf	ch_dot_index,f		; dec dot-index
		return							

;--------		
PrintDisp	call	ClearDisplay		; clear display content
		bsf	STATUS, RP0		; Bank1
		clrf	EEADR			; start of EE-memory
		movlw	0xE4			; Start for first char a 7 o'clock
		movwf	FSR			;
		movlw	.14			; 14 char to display
		movwf	Scratch3		;
pdisp_1				
		bsf	EECON1, RD		; read character from EEProm
		incf	EEADR,f			; inc address for next read
		movf	EEDATA,w		;

		bsf	STATUS,RP1		; goto Bank2
		bcf	STATUS,RP0		;

		call	CharOffset		; call character data
pdisp_2		
		call	LoadChrData		; load pixel data from CharGen
		movwf	INDF			; store char pixel data in display memory
		incf	FSR,w		; increment FSR
		call 	CheckIncrement	;
		movwf	FSR		;				
		
		decfsz	ch_dot_index,f		; 6 dot's in one character, all displayed ?
		goto	pdisp_2			;

		bcf	STATUS,RP1		; leave bank 2, back to bank1
		bsf	STATUS,RP0		;
			
		decfsz	Scratch3,f		; 						
		goto	pdisp_1				
		
		bcf	STATUS,RP0		; go back to bank 0
		return	
		
;--------
ScrollText	bsf	STATUS,RP1		; goto Bank2
		bcf	STATUS,RP0		;
      		
      		movf	ch_dot_index,w		;
		btfss	STATUS,Z		; dot_index == 0 ?
		goto	Scroll_0 
		
		movf	ch_blanking,w		;
		btfsc	STATUS,Z		;
		goto	Scroll_read_ee		;
		decfsz	ch_blanking,f		;
		goto	Scroll_2		; insert one more " "
		btfss	fDemo			; in demo mode?
		goto	Scroll_read_ee		; re-read char
		
		bcf	STATUS,RP1		; to bank 0
		call	TextON_OFF		; at end of line, turn text off!
		return				; stop scrolling
		
		
Scroll_read_ee	bcf	STATUS,RP1		; leave bank 2, to bank1
		bsf	STATUS,RP0		;
		bsf	EECON1, RD		; read character from EEProm
		incf	EEADR,f			; inc address for next read
		bcf	EEADR,7			; roll over after 0x80 => 0x00
		movf	EEDATA,w		;

		btfsc	STATUS,Z		; Chr$(0) indicates end of line
		clrf	EEADR			; start of EE-memory
		bsf	STATUS,RP1		; go back to Bank2
		bcf	STATUS,RP0		;

		btfss	STATUS,Z		; if Z was 1, it wil still be 1 here 
		goto	Scroll_1		;
		movlw	0x0F			; insert 15 " " at end of string to clear display
		movwf	ch_blanking		;
Scroll_2
		movlw	" "			; w = " "
Scroll_1

		call	CharOffset		; call character data
Scroll_0
		call	LoadChrData		; load pixel data from CharGen
		movwf	Scratch			;		

#ifdef ReversTextScroll
		movlw	0xE4			; Start at begin of 7 o'clock character
#else
		movlw	0xBF			; Start at end of 5 o'clock character
#endif
		movwf	FSR			;
ScrollLoop
		movf	INDF,w			; load current data
		movwf	Scratch2		; temp store
		movf	Scratch,w		; 
		movwf	INDF			; 
		movf	Scratch2,w		; shift data
		movwf	Scratch			;
		
#ifdef ReversTextScroll
		incf	FSR,w			; 
		call	CheckIncrement
		movwf	FSR			;						
		movlw	.14			; check end of 5 o'clock character
#else
		decf	FSR,w			; 
		call	CheckDecrement
		movwf	FSR			;						
		movlw	0xE3			; check begin of 7 o'clock character
#endif
		subwf	FSR,w			;
		btfss 	STATUS,Z		; FSR = 0xE4 ? (or 0xC0)
		goto	ScrollLoop		;

		bcf	STATUS,RP0		; goto Bank0
		bcf	STATUS,RP1		; 

		return				;

;DemoMode	btfsc	fScrollOn		; in scroll mode?
;		goto	Demo_0			; yes, already in scroll mode
;		
;		movf	Second,w		;
;		btfsc	STATUS,Z		;
;		bsf	fSCrollOn		;	
;		return
;		
;Demo_0		
;
;		return				;

DemoMode	movf	Second2,w		;
		btfsc	STATUS,Z		;
		call	TextON_OFF
		return
		
				
;******************************************************************************
;	Some general functions
;******************************************************************************
				
;- check correct decrement of Display memory pointer
CheckDecrement
		xorlw	0x1F		;
		btfsc	STATUS,Z	;
		movlw	0xEF ^ 0x1F	;
		xorlw	0x9F ^ 0x1F	;
		btfsc	STATUS,Z	;
		movlw	0x47 ^ 0x9F	;
		xorlw	0x9F		;
		return			;

;- check correct increment of Display memory pointer
CheckIncrement
		xorlw	0x48		;
		btfsc	STATUS,Z	;
		movlw	0xA0 ^ 0x48	;
		xorlw	0xF0 ^ 0x48	;
		btfsc	STATUS,Z	;
		movlw	0x20 ^ 0xF0	;
		xorlw	0xF0		;
		return			;
		
				
	END    ; directive 'end of program'
