// Calendar Clock
//
// PIC16F648A
//
//------------- versions
//
// 100_cal original
// 101_cal centered top line
// cal20090823b  formats (not julian) month advance (but not day suppression)
// 1-2_cal missing 16 seg test
// 150_cal release candidate
// 151_cal tested and 30 second timeout, use for RED and GREEN
// 152_cal fixed minute update for upper two time display modes
//
//----------------
//
// Pin connections
//
// RA0 (17) out  SCL
// RA1 (18) out SDA1
// RA2 (1)  out  SDA2
// RA3 (2)  out testbit
// RA4 (3)  in 32KHz Clock
// RA5 (4)  in power sense H=power L=loss of power
// RA6 (15) open
// RA7 (16) open

// RB0 (6)  open
// RB1 (7)  out to MCLR of slave PIC
// RB2 (8)  out to RX (pin-7) of slave PIC
// RB3 (9)  in pushbutton SW0 UP
// RB4 (10) in pushbutton SW1 DOWN
// RB5 (11) in pushbutton SW2 ENTER
// RB6 (12) in pushbutton SW3 MODE
// RB7 (13) open  

// data direction registers  1=input
// trisa = 00110000 0x30
// trisb = 01111000 0x78

// 32.768KHz on TMR0 input
// 

#include <system.h>
#include <string.h>

#pragma CLOCK_FREQ 4000000	

#pragma DATA _CONFIG , _INTOSC_OSC_NOCLKOUT & _WDT_OFF & _LVP_OFF &_MCLRE_OFF  & _BODEN_OFF
	
unsigned char runflag; // true to run time
volatile unsigned char cnt_p125;	// prescaler
volatile unsigned char cnt_seconds; // advanced in interrupt
volatile unsigned char cnt_minutes; // advanced in interrupt
volatile unsigned char cnt_hours; // advanced in interrupt
unsigned long b1;
unsigned long julian; // advanced in interrupt
unsigned long savejulian; // advanced in interrupt

unsigned long b2;
volatile unsigned char day; // global var
unsigned long b3;
volatile unsigned char month; // global var
unsigned long b4;
volatile unsigned int year;// global var
volatile unsigned char cap; // true when running on cap
unsigned long b5;
unsigned char dtype; // display format
unsigned char flipflag; // true if flipping thru months

enum TMODE_ENUM { TMODE_POWERUP=0,TMODE_RUN,TMODE_DTYPE,TMODE_SETYEAR,TMODE_SETMONTH,
		TMODE_SETDAY,TMODE_SETHOUR,TMODE_SETMINUTE,
		TMODE_PRESET, TMODE_TEST, TMODE_LAST};
enum TMODE_ENUM top_mode,last_mode;

volatile unsigned char sw0; // 0 when up pressed
volatile unsigned char sw1; // 0 when down pressed
volatile unsigned char sw2; // 0 when mode pressed

char line[17]; // this is 17 long to make room for line terminator 0

signed char sw0cnt; // debounce counter
signed char sw1cnt; // debounce counter
signed char sw2cnt; // debounce counter
signed char capcnt; // debounce counter

// 
// 128Hz interupt
//
void interrupt( void )
{

// inc time
    cnt_p125++;

if (cnt_p125>127)
{
    cnt_p125=0;
		cnt_seconds++;
		if (cnt_seconds>59) 
		{
			cnt_seconds=0;
			if(runflag!=0)
			{
				cnt_minutes++;
				if (cnt_minutes>59)
				{
					cnt_minutes=0;
					cnt_hours++;
					if (cnt_hours>23)
					{
						cnt_hours=0;
						julian++;
					}
				}
			}
		}
	
}


// switches
if (portb.4==1)
{
    if(sw0==0)
    {
		if(sw0cnt<3)
		{
			sw0cnt++;
		}
		else
		{
			sw0=1;
		}
    }
}
else
{
    if(sw0==1)
    {
		if(sw0cnt>0)
		{
			sw0cnt--;
		}
		else
		{
	        sw0=0;
		}
    }
}

if (portb.3==1)
{
    if(sw1==0)
    {
		if(sw1cnt<3)
		{
			sw1cnt++;
		}
		else
		{
	        sw1=1;
		}
    }
}
else
{
    if(sw1==1)
    {
		if(sw1cnt>0)
		{
			sw1cnt--;
		}
		else
		{
	        sw1=0;
		}
    }
}


if (portb.5==1)
{
    if(sw2==0)
    {
		if(sw2cnt<3)
		{
			sw2cnt++;
		}
		else
		{
	        sw2=1;
		}
    }
}
else
{
    if(sw2==1)
    {
		if(sw2cnt>0)
		{
			sw2cnt--;
		}
		else
		{
	        sw2=0;
		}
    }
}


// cap
if (porta.5==1)
{
    if(cap==1)
    {
		if(capcnt<3)
		{
			capcnt++;
		}
		else
		{
	        cap=0;
		}
    }
}
else
{
    if(cap==0)
    {
		if(capcnt>0)
		{
			capcnt--;
		}
		else
		{
			cap=1;
		}
    }
}



//clear_bit( porta, 3 );
	clear_bit( intcon, T0IF );  //clear TMR0 overflow flag
}


// i2c connection routines, have these next 5 call bit and delay routines

 void sda_high(void)
{
    set_bit( porta, 1 );      
}

 void sda_high2(void)
{   
    set_bit( porta, 2 );   
}


 void sda_low(void)
{
    clear_bit( porta, 1 );
}

 void sda_low2(void)
{
    clear_bit( porta, 2 );
}


 void scl_high(void)
{
    set_bit( porta, 0 );
}


 void scl_low(void)
{
    clear_bit( porta, 0 );
}

 void i2c_delay()
{
	delay_us(10);
}

// i2c routines

void i2c_start(void)
{
    scl_high();
    sda_high();
    sda_high2();
    i2c_delay();    
    sda_low();
    sda_low2();
    i2c_delay();
    scl_low();
    i2c_delay();
}

void i2c_stop(void)
{
    sda_low();
    sda_low2();
    scl_low();  
    i2c_delay();      
    scl_high();
    i2c_delay();
    sda_high();
    sda_high2();
    i2c_delay();
}


void i2c_bitout(unsigned char b,unsigned char c)
{
    // set data bit
    if (b!=0)
    {
	    sda_high();
	}
    else
    {
	    sda_low();
	}
    // set data bit
    if (c!=0)
    {
	    sda_high2();
	}
    else
    {
	    sda_low2();
	}
    // pulse clock
    i2c_delay();
    scl_high();
    i2c_delay();
    scl_low();
    i2c_delay();
}

void i2c_ack()
{
    i2c_delay();
    scl_high();
    i2c_delay();
    scl_low();
    i2c_delay();
}

void i2c_byteout(unsigned char b,unsigned char c)
{
    i2c_bitout(b&0x80,c&0x80);
    i2c_bitout(b&0x40,c&0x40);
    i2c_bitout(b&0x20,c&0x20);
    i2c_bitout(b&0x10,c&0x10);
    i2c_bitout(b&0x08,c&0x08);
    i2c_bitout(b&0x04,c&0x04);
    i2c_bitout(b&0x02,c&0x02);
    i2c_bitout(b&0x01,c&0x01);
    i2c_ack();			// ack cycle


}

//
// high and low bytes for 16 segment displays starting 
//
rom unsigned char *segments ={

162	,	214	,
2	,	2	,
132	,	215	,
134	,	151	,
38	,	3	,
166	,	149	,
166	,	213	,
2	,	22	,
166	,	215	,
166	,	151	,
128	,	4	,
64	,	16	,
9	,	0	,
4	,	21	,
64	,	32	,
4	,	30	,
164	,	215	,
38	,	87	,
150	,	158	,
160	,	212	,
146	,	158	,
164	,	213	,
36	,	85	,
166	,	212	,
38	,	67	,
144	,	156	,
130	,	194	,
41	,	65	,
160	,	192	,
42	,	98	,
35	,	98	,
162	,	214	,
36	,	87	,
163	,	214	,
37	,	87	,
166	,	149	,
16	,	28	,
162	,	194	,
104	,	64	,
99	,	66	,
73	,	32	,
36	,	11	,
200	,	148	,
32	,	208	,
1	,	32	,
130	,	6	,
72	,	0	, // changed to / from ^
128	,	128	,




};

unsigned int asc_to_bits(unsigned char ic)
{
unsigned int c;
unsigned char *p;
unsigned int c16;

	c=ic;
	// out of bounds = "@"
	if ((c<0x30)||(c>0x5f))
	{
		c16=0;
	}
	else
	{
		// zero base pointer
		p=c-0x30;
		// two bytes per character so double pointer offset
		p=p<<1;
		// retrieve 16 bits
		c16=(segments[p])<<8 | segments[p+1];
	}
	return(c16);
}


//
// send 16 characters from line[]
//
void send_string()
{

char a,a2;
unsigned int l1;
unsigned int l2;
unsigned int r1;
unsigned int r2;

// sent in pairs of quad bytes, each quad fills one display chip SAA1064
for(a=0;a<4;a++)
{

	i2c_start();
	switch(a)
    	{
	case 0:
		i2c_byteout(0x70,0x70); // address
		break;
	case 1:			
		i2c_byteout(0x72,0x72); // address
		break;
	case 2:			
		i2c_byteout(0x74,0x74); // address
		break;
	default:			
		i2c_byteout(0x76,0x76); // address
		break;
	}	
	a2=a*2;
	l1=asc_to_bits(line[a2]);
	l2=asc_to_bits(line[(a2)+1]);
	r1=asc_to_bits(line[(a2)+8]);
	r2=asc_to_bits(line[(a2)+9]);	
	i2c_byteout(0x00,0x00); // instruction	
	i2c_byteout(0x67,0x67); // control 
	i2c_byteout(l1>>8,r1>>8); // high bits
	i2c_byteout(l2>>8,r2>>8); // high bits	
	i2c_byteout(l1& 0xff,r1& 0xff); // low bits
	i2c_byteout(l2& 0xff,r2& 0xff); 
	i2c_stop();
}
}

void blankline()
{
unsigned char i;
for(i=0;i<17;i++)
	line[i]=0;
}

//
// fill line
//
void fill_line(rom char *s)
{
blankline();
strcpy(line,s);                   // copy string into line
}

void fill_line_send(rom char *s)
{
	fill_line(s);
	send_string();
}


// make 2 digit ascii
void sprint2dig(char *l,unsigned char d)
{
char c;
	c=d/10;
	*l=0x30|c;
	c=d%10;
	l++;
	*l=0x30|c;
}

// make 2 digit ascii
//
// returns 1 if blanking occured
//
char sprint2digblank(char *l,unsigned char d)
{
char c;
char rv;
rv=0;
	c=d/10;
	if(c>0)
	{
		*l=0x30|c;
		l++;
	}
	else
		rv=1;
	c=d%10;

	*l=0x30|c;
	return(rv);
}

void sprint2dig_send(char *l,unsigned char d)
{
	sprint2dig(l,d);
	send_string();
}



//
// convert julian -> month day year
//
void  julianunpack()
{
unsigned long p;
//unsigned long q;
unsigned int q;
unsigned long r;
//unsigned long s;
unsigned int s;
unsigned long t;
unsigned long u;
unsigned long v;

// p = j + 68569
p=julian+68569;
//   q = int(4*p/146097)
q=(4*p)/146097;
//   r = p - int((146097*q + 3)/4)
r= p- ((146097*q)+3)/4;
//   s = int(4000*(r+1)/1461001)
s = (4000*(r+1))/1461001;
//   t = r - int(1461*s/4) + 31
t = r - (1461*s/4) + 31;
//   u = int(80*t/2447)
u = (80*t)/2447;
//   v = int(u/11)
v = u/11;

//     y = 100*(q-49)+s+v
year = 100*(q-49)+s+v;
//     m = u + 2 - 12*v
month = u + 2 - 12*v;
//     d = t - int((2447*u)/80)
day = t - (2447*u)/80;

}


//
// convert  month day year -> julian
//
void julianpack()
{
unsigned long a;
unsigned long b;
//unsigned long c;
unsigned int c;
unsigned long d;
unsigned long e;
unsigned long f;
unsigned long v;

// j = 367*y - int(7*(y + int((m+9)/12)) /4) + int((275*m)/9) + d + 1721014

a = 367*year ;
b = (7*(year + ((month+9)/12))) /4;
c = (275*month)/9;
d = day + 1721014;

julian = a - b + c + d;

}

//
// send byte out serial
//
void sendbyte(unsigned char c)
{
	// wait for transmit buffer empty
	while(txsta.1==0)
	{
	}
	txreg=c;
}
//
// commands to slave
// 0n decode:
// 
// 0	low byte
// 1	high byte
// 2	set first day
// 3	set month size
// 4	set highlight
// 5	do op  0=stop 1=blank 2=fill 3=count 3=seg test
//
// 
//

void sendbytemsg(unsigned char v)
{
	sendbyte(v&0x0f);		// low nybble
	sendbyte(0x10|(v>>4));	// high nybble
}

rom char *st_jan="JANUARY";
rom char *st_feb="FEBRUARY";
rom char *st_mar="MARCH";
rom char *st_apr="APRIL";
rom char *st_may="MAY";
rom char *st_jun="JUNE";
rom char *st_jul="JULY";
rom char *st_aug="AUGUST";
rom char *st_sep="SEPTEMBER";
rom char *st_oct="OCTOBER";
rom char *st_nov="NOVEMBER";
rom char *st_dec="DECEMBER";


// month offsets
rom unsigned char *mstart = { 1,0,2,2,3,2,2,1,0,1,0,0};
// alternate month offsets
rom unsigned char *amstart = { 1,0,2,2,3,3,3,2,0,1,0,0};
// dystart
rom unsigned char *dystart = { 9,9,8,8,7,7,7,8,10,9,9,9};

//
// place month in line[] stating at offset
//
void placemonth(unsigned char moff)
{

rom unsigned char *rucptr;
switch(month)
{
case 1:
	rucptr=st_jan;
	break;
case 2:
	rucptr=st_feb;
	break;
case 3:
	rucptr=st_mar;
	break;
case 4:
	rucptr=st_apr;
	break;
case 5:
	rucptr=st_may;
	break;
case 6:
	rucptr=st_jun;
	break;
case 7:
	rucptr=st_jul;
	break;
case 8:
	rucptr=st_aug;
	break;
case 9:
	rucptr=st_sep;
	break;
case 10:
	rucptr=st_oct;
	break;
case 11:
	rucptr=st_nov;
	break;
case 12:
	rucptr=st_dec;
	break;
	}
strcpy(line+moff,rucptr);
}


//
// place 24 hour time at line[11]
//
place_24hrtime()
{
			sprint2dig(line+11,cnt_hours);
			line[13]=':';			
			sprint2dig(line+14,cnt_minutes);	
}


//
// place 12 hour time at line[off]
//
place_12hrtime(unsigned char toff)
{
unsigned char hr;
			
	hr=cnt_hours%12;
	if(hr==0) hr=12;
	// hours
	if(hr>9)
	{
		sprint2dig(line+toff,hr);		
	}
	else
	{
		line[toff]=' ';
		line[toff+1]=0x30|hr;
	}
	// colon
	line[toff+2]=':';			
	// minutes
	sprint2dig(line+toff+3,cnt_minutes);	
	// AM or PM suffix
	if(cnt_hours<12)
		line[toff+5]='A';			
	else
		line[toff+5]='P';	
	line[toff+6]='M';		
}



//
// place date string mm/dd/yyyy
//
// yyyy if flag true
//
void place_mmddyyyy(unsigned char flag)
{
unsigned char i;

	if(sprint2digblank(line,month))
		i=1;
	else
		i=2;
	line[i++]='^'; // will display  '/' character
	if(sprint2digblank(line+i,day))
		i++;
	else
	{
		i++;
		i++;
	}
	line[i++]='^'; // will display  '/' character
	if(flag)
	{
		// place yyyy
		sprint2dig(line+i,year/100);
		sprint2dig(line+i+2,year%100);	
	}
	else
	{
		// place yy
		sprint2dig(line+i,year%100);	
	}
}


//------------------------------------------------------------------------
//
//  Format top line according to dtype
//
// display types
// 0 <month> <date> <year>
// 1 <month> <year>
// 2 <month> 
// 3 <month> <24 hr time>
// 4 <month> <12 hr time>
// 5 mm/dd/yyyy
// 6 mm/dd/yyyy 24ht
// 7 mm/dd/yy 12ht
//

void showtopline()
{
unsigned char hr;
unsigned char moff;
unsigned char mdisp;
unsigned char ddisp;
unsigned char ydisp;
unsigned char madisp;
unsigned char y_h;
unsigned char y_l;
unsigned char ldtype;

moff=month-1;
madisp=amstart[moff];
mdisp=mstart[moff];
ddisp=dystart[moff];
// compute year offset
ydisp=ddisp+2;
if(day>9)
	ydisp++;	

blankline();
y_h=year/100;
y_l=year%100;

// show month year if flipflag
if(flipflag)
	ldtype=1;
else
	ldtype=dtype;
switch(ldtype)
	{
	case 0: // month dd yyyy
		placemonth(mdisp);
		sprint2digblank(line+ddisp,day);
		if((month==9)&&(day>9))
		{
			// shorten year
			sprint2dig(line+ydisp,y_l);		
		}
		else
		{
			// normal display
			sprint2dig(line+ydisp,y_h);
			sprint2dig(line+ydisp+2,y_l);
		}
		break;
	case 1: // month yyyy
		placemonth(mdisp+1);
		sprint2dig(line+ddisp+1,y_h);
		sprint2dig(line+ddisp+3,y_l);
		break;
	case 2: // month
		placemonth(mdisp+4);
		break;
	case 3: // month 24hr
		placemonth(madisp+1);
		place_24hrtime();
		break;
	case 4: // month 12hr
		placemonth(madisp+0);
		hr=cnt_hours%12;
		if(hr==0) hr=12;
		if((month==9)&&(hr>9))
			place_12hrtime(10);
		else
			place_12hrtime(9);
		break;
	case 5: //  mm/dd/yyyy
		place_mmddyyyy(1);	
		break;
	case 6: // mm/dd/yyyy  24hr
		place_mmddyyyy(1);	
		place_24hrtime();
		break;
	case 7: // mm/dd/yy  12hr	
		place_mmddyyyy(0);
		place_12hrtime(9);
		break;
	break;
	}
	send_string();
}


//****************************************************************
//
void main()
{
unsigned char oldsec,oldmin; 
unsigned long old_julian;
unsigned char oldsw1,oldsw2,oldsw3,oldsw0;
unsigned char oldcap;
unsigned int dis;

unsigned char timer;
unsigned char msize;
unsigned char tc;

rom char *st_setyear="SET YEAR";
rom char *st_setmonth="SET MONTH";
rom char *st_setday="SET DAY";
rom char *st_sethour="SET HOUR";
rom char *st_setmin="SET MINUTE";
rom char *st_preset="PRESET";
rom char *st_test="SELECT TEST";
rom char *st_kabtr="KABTRONICS";
rom char *st_count="0123456789ABCDEF";

// set port directions
trisa = 0x30;	// porta direction
trisb = 0x78;	// portb direction 
cmcon = 0x07;	// turn off comparitors
clear_bit( option_reg, 7 ); // turn on portb pullups
// turn on serial transmiter
spbrg = 25;		// 2400 BAUD
txsta = 0x20; 	// TXEN = 1, 8 bit, async
rcsta = 0x80;	// SPEN = 1


// clear time and state variables

top_mode=TMODE_POWERUP;
last_mode=TMODE_SETDAY;
cnt_p125=0;
cnt_seconds=0;
cnt_minutes=20;
cnt_hours=8; 
julian=2455044; // 
month=0; // will be set by julianunpack();
day=0; // will be set by julianunpack();
year=0; // will be set by julianunpack();
sw0=1; // switches go to 0 when pressed
sw1=1;
sw2=1;
sw0cnt=0; // these are switch debounce filters used in interrupt
sw1cnt=0;
sw2cnt=0;
oldsec=78;
oldmin=0;
timer=0;
dis=0;
old_julian=0;
cap=0;
capcnt=0;
oldcap=0;
flipflag=0;

oldsw1=1;
oldsw2=1;
oldsw0=1;
dtype=0; 

runflag=1; // enable time keeping

// enable interrupts
set_bit( intcon, T0IE ); //enable TMR0 overflow bit
set_bit( intcon, GIE ); // start interrupts

while( 1 )
{

if(oldcap!=cap) // change in power state
//if(0)
{
	oldcap=cap;
	if(cap==1) // power loss, running on cap
	{
	// turn off RS232
//	rcsta = 0x00;	// SPEN = 0
	// set outputs low to prevent driving a dead circuit
//	porta=0x30;   
//	portb=0xf9;  	

// RA0 (17) out  SCL						out
// RA1 (18) out SDA1						out
// RA2 (1)  out  SDA2						out
// RA3 (2)  out testbit						out
// RA4 (3)  in 32KHz Clock					in
// RA5 (4)  in power sense					in
// RA6 (15) open							out
// RA7 (16) open							out

// RB0 (6)  open							out
// RB1 (7)  out to MCLR of slave PIC		out
// RB2 (8)  out to RX (pin-7) of slave PIC	out
// RB3 (9)  in pushbutton SW0 UP			out
// RB4 (10) in pushbutton SW1 DOWN			out
// RB5 (11) in pushbutton SW2 ENTER			out
// RB6 (12) in pushbutton SW3 MODE			out
// RB7 (13) open  							out

// data direction registers  1=input
// trisa = 00110000 0x30
// trisb = 00000000 0x00


txsta = 0x00; 	// TXEN = 1, 8 bit, async
trisa = 0x30;	// porta direction
trisb = 0x00;	// portb direction 
porta = 0x30;
portb = 0x00;
	}
	else // power restored
	{
	// restore RS232
	txsta = 0x20; 	// TXEN = 1, 8 bit, async	
	trisa = 0x30;	// porta direction
	trisb = 0x78;	// portb direction 	
	
	
	old_julian=0; // force an update
	top_mode=TMODE_POWERUP; // display power up
	last_mode=TMODE_SETDAY; // force powerup state
	}
}


if(cap==0) // power on

{

// unpack julian into global day,month,year if julian changed
if (old_julian!=julian)
{
unsigned char fd;
	old_julian=julian;
	julianunpack();
	switch(month)
	{

		case 1:
		case 3:
		case 5:
		case 7:
		case 8:
		case 10:
		case 12:
			msize=31;	
			break;
		case 4:
		case 6:
		case 9:
		case 11:
			msize=30;	
			break;
		case 2:
		// figure out size of this feb 	
			if(year&3)
				msize=28;
			else
				msize=29;			
			break;	
	}
	
	if(top_mode==TMODE_RUN)
		showtopline();

	// calc offset
	fd= (julian-day)%7+2;
	fd=fd%7;

	sendbytemsg(msize);			
	sendbyte(0x30);	// set month size		
	sendbytemsg(fd);			
	sendbyte(0x20);	// set first day 1-37	
	if ((top_mode==TMODE_RUN)&&(timer!=0))	// turn off highlight if flipping months
		sendbytemsg(0);	
	else
		sendbytemsg(day);	
	sendbyte(0x40); // set highlite	
	if(top_mode==TMODE_RUN||
		top_mode==TMODE_SETYEAR||
		top_mode==TMODE_SETMONTH||
		top_mode==TMODE_SETDAY)
			sendbyte(0x52); // fill		

}


switch(top_mode)
{
case TMODE_POWERUP:
	if (last_mode!=top_mode)
	{
		timer=5;
		last_mode=top_mode;
		blankline();
		strcpy(line+3,st_kabtr);
		send_string();	
	}
	break;
case TMODE_RUN:
	if(!runflag)
		runflag=1;
	if (last_mode!=top_mode)
	{
		last_mode=top_mode;

		oldmin=65;
		sendbyte(0x52);			
		showtopline();	
	}
	break;
case TMODE_DTYPE:
	if(!runflag)
		runflag=1;
	if (last_mode!=top_mode)
	{
		if (flipflag)	
		{
			julian=savejulian;
			flipflag=0;
		}
		timer=30;
		blankline();
		last_mode=top_mode;
		strcpy(line,"SELECT FORMAT");
		oldmin=65;  // force update
		send_string();	
		sendbyte(0x52);	
	}
	break;	
case TMODE_SETYEAR:
	if (last_mode!=top_mode)
	{
		timer=30;
		last_mode=top_mode;
		fill_line(st_setyear);
		sprint2dig(line+14,year%100);
		sprint2dig_send(line+12,year/100);		
	}
	break;
case TMODE_SETMONTH:
	if (last_mode!=top_mode)
	{
		timer=30;
		last_mode=top_mode;
		fill_line(st_setmonth);
		sprint2dig_send(line+14,month);
	}
	break;
case TMODE_SETDAY:
	if (last_mode!=top_mode)
	{
		timer=30;
		last_mode=top_mode;
		fill_line(st_setday);
		sprint2dig_send(line+14,day);	
	}
	break;
case TMODE_SETHOUR:
	if (last_mode!=top_mode)
	{
		timer=30;
		runflag=0; // stop clock
		last_mode=top_mode;
		fill_line(st_sethour);
		sprint2dig_send(line+14,cnt_hours);		
	}
	break;
case TMODE_SETMINUTE:
	if (last_mode!=top_mode)
	{
		timer=30;
		runflag=0; // stop clock
		last_mode=top_mode;
		fill_line(st_setmin);
		sprint2dig_send(line+14,cnt_minutes);		
	}
	break;
case TMODE_PRESET:
	if (last_mode!=top_mode)
	{
		timer=30;
		last_mode=top_mode;
		fill_line_send(st_preset);		
	}
	break;
case TMODE_TEST:
	if (last_mode!=top_mode)
	{
		last_mode=top_mode;
		fill_line_send(st_test);		
	}
	break;
	}


// run the clock display if time is being shown
if((top_mode==TMODE_RUN)&&((dtype==3)||(dtype==4)||(dtype==6)||(dtype==7)))
{
	if(oldmin!=cnt_minutes)
	{
		oldmin=cnt_minutes;
		showtopline();
	}
}



// check inactivity timer	


if (top_mode!=TMODE_TEST)
{
	if((!sw0)||(!sw1)||(!sw2)) // if any switch pressed
	{
		if(timer)
			timer=30;
	}
}
	

	if((timer>0)&&(top_mode!=TMODE_TEST))
	{
		if (oldsec!=cnt_seconds)
		{
			oldsec=cnt_seconds;
			timer--;
			if(!timer)
			{

					if(flipflag)
					{
					// restore original date
						julian=savejulian;
						flipflag=0;
					}
					top_mode=TMODE_RUN;			
			}
		}
	}
	
	
// up switch
	if(oldsw0!=sw0)
	{
	    oldsw0=sw0;
	    if (!sw0)
	    {
	    unsigned long j;
		switch(top_mode)
		{
			case TMODE_PRESET:
					top_mode=TMODE_POWERUP;
					cnt_p125=0;
					cnt_seconds=0;
					cnt_minutes=0;
					cnt_hours=11;
					julian=2455039; // july 27 2009 
					break;
			case TMODE_SETDAY:
					day++;
					julianpack();
					julianunpack();
					sprint2dig_send(line+14,day);	
					break;
			case TMODE_SETYEAR:
					year++;
					julianpack();
					julianunpack();
					sprint2dig(line+14,year%100);
					sprint2dig_send(line+12,year/100);	
					break;
			case TMODE_SETMONTH: 
					if(month<12)
					{
					month=month+1;
					if((month==2)&&(day>28))
						day=28;
					julianpack();
					julianunpack();
					sprint2dig_send(line+14,month);	
					}
					break;
			case TMODE_SETHOUR:
					cnt_hours++;
					if(cnt_hours>23)
						cnt_hours=0;
					sprint2dig_send(line+14,cnt_hours);	
					break;
			case TMODE_SETMINUTE:
					cnt_minutes++;
					if(cnt_minutes>59)
						cnt_minutes=0;
					sprint2dig_send(line+14,cnt_minutes);	
					break;
			case TMODE_DTYPE:
					if(dtype<7)
						dtype++;
					else
						dtype=0;	
					showtopline();
					break;		
			case TMODE_RUN:	
					// check if switching to flip page mode
					if (!flipflag)
					{
						flipflag=1;
						timer=30; // set countdown timeer
						savejulian=julian; // save this date
						day=1; // avoid month length problems
					}
					if(month<12)
						month++;	
					else
					{
						month=1;
						year++;
					}
					julianpack();
					julianunpack();
//					showtopline();					
					break;									
			case TMODE_TEST:
					sendbyte(0x53);
					strcpy(line,st_count);
					send_string();
					break;
			default:
					break;
	    } // switch
	    } // if
	}
	
// down switch
	if(oldsw1!=sw1)
	{
	    oldsw1=sw1;
	    if (sw1==0)
	    {
		switch(top_mode)
		{
			case TMODE_SETDAY:	    
				if(day>1)
				{
					day--;
					julianpack();
					julianunpack();
					sprint2dig_send(line+14,day);	
				}
				break;
			case TMODE_SETYEAR:	    
				if(year>2000)
				{
					year--;
					julianpack();
					julianunpack();
					sprint2dig(line+14,year%100);
					sprint2dig_send(line+12,year/100);
				}
				break;
			case TMODE_SETMONTH:	    
				if(month>1)
				{
					month--;
					if((month==2)&&(day>28))
						day=28;					
					julianpack();
					julianunpack();
					sprint2dig_send(line+14,month);	
				}
				break;
			case TMODE_SETHOUR:
					if (cnt_hours==0)
						cnt_hours=23;
					else
						cnt_hours--;
					sprint2dig_send(line+14,cnt_hours);	
					break;
			case TMODE_SETMINUTE:
					if (cnt_minutes==0)
						cnt_minutes=59;
					else
						cnt_minutes--;
					sprint2dig_send(line+14,cnt_minutes);	
					break;									
			case TMODE_DTYPE:
					if(dtype>0)
						dtype--;
					else
						dtype=7;
					showtopline();											
					break;					
			case TMODE_RUN:					
					// check if switching to flip page mode
					if (!flipflag)
					{
						flipflag=1;
						timer=30; // set countdown timer
						savejulian=julian; // save this date
						day=1; // avoid month length problems
					}
					if(month>1)
						month--;	
					else
					{
						month=12;
						year--;
					}
					julianpack();
					julianunpack();
//					showtopline();					
					break;									
			case TMODE_TEST:
					sendbyte(0x54);
					break;
			default:
					break;
			
	    } // switch
	    } // if
	    
	}
	
	
// MODE switch
	if(oldsw2!=sw2)
	{
	    oldsw2=sw2;
	    if (sw2==0)
	    {
		top_mode++;	
		if(top_mode>=TMODE_LAST)
			top_mode=TMODE_RUN;    
		}
	}	
	
}
	
	delay_ms(50);
    
} //endless while loop
} // program end
