#include <eZ8f0812.h>
//#include "Z8FakePorts.h"
//extract X1226 code
//extract LED brightness sensing code
//extract setTime function, create defines for case values

#define I2C_TDRE  0x80
#define I2C_RDRF  0x40
#define I2C_ACK   0x20
#define I2C_STOP  0x20
#define I2C_START 0x40
#define I2C_DSS   0x02

#define MAX_SETTIME_LOOPS 4

//build options
#undef SEC_BLINK
#undef ALTERNATING_COLON
#undef USE_DOW


// xpdf /w98/Program\ Files/ZiLOG/ZDSII_Z8Encore\!_4.7.0/Documentation/Application_Notes/Using\ the\ Z8\ Encore\!\ MCU\ as\ an\ I2C\ Bus\ Master\ Application\ Note\ AN0126.pdf


/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* To Do:
*
* FIX RTC Read bug!
*  //Digital trim in SetTime - Red to slow clock, Green to speed up - Inc or Dec current setting in RTC
*  Store year from last SetTime() in RTC Flash, retrieve during SetTime():SetYear
*  Quick DST mode to start/stop DST - 10Hr alternates red/green for stop/start with
*   1H, 10M and 1M light blue suggesting sky. i.e. Start/stop Daylight time
*  Improve SetTime logic ex.: QuickDST terminates SetTime, ability to change time only,  
*  Last GPIO as audible alarm output (Try PWM for DAC, waveform in X1226 Flash)
*  Adaptive brightness
*  Use Z8 as Freq. counter to test 32768Hz crystal - adjust digital trim to compensate
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

/*
   76543210  (d) display
PA ssccbbId  (s)da/(s)cl (c)olon (b)lueLED 
PB    ddddd
PC   dddddx

10h 
  R  C2
  G  A0
  Ba A3 --->|---|
  Bc A2 --------|

 1h
  R  C4
  G  C3
  B  C5

Colon
  Upper A4
  Lower A5

10m
  R  B4
  G  C1
  B  B3

 1m
  R  B2
  G  B1
  B  B0

RTC 
  #Irq  A1
  SDA   A7
  SCL   A6

N/C  C0

PAMasks[] = c10Hour: 0x01 + colon: 0x30
PBMasks[] = c10Minute: 0x18 + c1Minute: 0x07
PCMasks[] = c1Hour: 0x38 + c10Hour: 0x04 + c10Minute: 0x02

*/
#define R10H  0x40000
#define R1H   0x100000
#define R10M  0x1000
#define R1M   0x400

#define G10H  0x1
#define G1H   0x80000
#define G10M  0x20000
#define G1M   0x200

#define B10HA 0x8
#define B10HC 0x4
#define B1H   0x200000
#define B10M  0x800
#define B1M   0x100

#define COLU 0x10
#define COLL 0x20

// 0x0a is sky blue for quickDST mode
//                           0     1     2     3     4     5     6     7     8    9     a
//                        black brown  red orange yellow green blue violet gray white  sky
unsigned char RED[0x0b] = {0x00, 0x02, 0x40, 0x35, 0x34, 0x00, 0x00, 0x10, 0x03, 0x40, 0x0c};
unsigned char GRN[0x0b] = {0x00, 0x01, 0x00, 0x0e, 0x2a, 0x40, 0x00, 0x00, 0x03, 0x40, 0x0c};
unsigned char BLU[0x0b] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x10, 0x05, 0x40, 0x40};

char Write_I2C(unsigned char *, unsigned char);
void I2C_Reset(void);
void Read_X1226(unsigned char, unsigned char, unsigned char, unsigned char *, unsigned char);
void Write_X1226(unsigned char, unsigned char, unsigned char, unsigned char *, unsigned char);
void Set_X1226_Write(char);
char SetTime(char);
void init(void);
//void Lamptest(void);
void DisplayISR(void);
void TickIRQ(void);
void T1IRQ(void);
void BrightIRQ(void);
void Buffer2Time(unsigned char *);
void SPI_BRG_IRQ(void);

//current time value
unsigned char cColon;
unsigned char c10Hour;
unsigned char c1Hour;
unsigned char c10Minute;
unsigned char c1Minute;
unsigned char bTimePM;
unsigned char cSeconds;
unsigned char c10Month;
unsigned char c1Month;
unsigned char c10Day;
unsigned char c1Day;

//force RTC read
unsigned char bGetDate = 0;

//brightness
unsigned char bBright = 0;

//display vars.
unsigned char cPWM = 0;
unsigned char PAVal = 0;
unsigned char PBVal = 0;
unsigned char PCVal = 0;

//coalesce
#ifndef SEC_BLINK
char secDelta = 0;
unsigned char displ_sec = 0;
#endif

char r1mDelta = 0;
unsigned char displ_r1m = 0;
char g1mDelta = 0;
unsigned char displ_g1m = 0;
char b1mDelta = 0;
unsigned char displ_b1m = 0;
char r10mDelta = 0;
unsigned char displ_r10m = 0;
char g10mDelta = 0;
unsigned char displ_g10m = 0;
char b10mDelta = 0;
unsigned char displ_b10m = 0;

char r1hDelta = 0;
unsigned char displ_r1h = 0;
char g1hDelta = 0;
unsigned char displ_g1h = 0;
char b1hDelta = 0;
unsigned char displ_b1h = 0;
char r10hDelta = 0;
unsigned char displ_r10h = 0;
char g10hDelta = 0;
unsigned char displ_g10h = 0;

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void main (void)
{
 unsigned char c;
 unsigned char x;
 unsigned char bDoSetClock = 0;
 unsigned char cI2CBuf[0x20];

 init();
 //LT
 Read_X1226(0xdf, 0x00, 0x30, (unsigned char *)&cI2CBuf[0], 8);
 //read RTC returns invalid values 7f(s) 7f(m) bf(h) 3f(dy) 1f(mo) ff(yr) 07(dow) 19(cen) for power failures (should check Power bit, too)
 if((cI2CBuf[0] > 0x59) || (cI2CBuf[1] > 0x59) || (cI2CBuf[2] > 0x39) || (cI2CBuf[3] > 0x31) || (cI2CBuf[4] > 0x12) || (cI2CBuf[5] > 0x99) || (cI2CBuf[6] > 0x06))
 {// if invalid values come in, reset clock paramaters and set date to 00:00:00 Mon. 01 Jan. 2007 (the date this was supposed to go live)
  Set_X1226_Write(3); //non-volatile, right?
  cI2CBuf[0] = 0x18;
  Write_X1226(0xde, 0x00, 0x11, (unsigned char *)&cI2CBuf[0], 1); //write 11h F[0,1], 18h - set PHZ for 1.0 Hz output
  cI2CBuf[0] = 0x00; //sec
  cI2CBuf[1] = 0x00; //min
  cI2CBuf[2] = 0x00; //hr |= 0x20 for PM
  cI2CBuf[3] = 0x01; //date
  cI2CBuf[4] = 0x01; //month
  cI2CBuf[5] = 0x07; //year
  cI2CBuf[6] = 0x01; //day o'week
  cI2CBuf[7] = 0x20; //y2k
  Write_X1226(0xde, 0x00, 0x30, (unsigned char *)&cI2CBuf[0], 1); //Set time 00:00:00 Mon. 01 Jan. 2007
  //Erase_X1226_Flash( 000 and 001);
  //write 2007 to Flash as Initial Year (IY)
  //cI2CBuf[0] = cI2CBuf[7]; cI2CBuf[1] = cI2CBuf[5];
  //Write_X1226(0xae, 0x00, 0x00, (unsigned char *)&cI2CBuf[0], 2); //Set time 00:00:00 Mon. 01 Jan. 2007
  //if(c < 0) asm("halt");//while(c < 0); //DEBUG
  Set_X1226_Write(0);
 }
 Buffer2Time((unsigned char *)&cI2CBuf[0]);
 PADD = 0x02;    //config port for normal operation (initial charge for the LED)
 PAOUT = 4;      //charge LED
 PADD = 0x06;    //config port for reading

 T1RH = 0x7a;  
 T1RL = 0x13;  // .200 sec.
 T1H = 0;
 T1L = 1; 
 T1CTL = 0xB8;   //1(enable) 0(polar) 111(presc) 000(mode)
 T0CTL = 0x89;   //1(enable) 0(polar) 001(presc) 001(mode)
 SPICTL = 0xa3;
 EI();

 for(;;)
 {
#ifndef SEC_BLINK
 if(displ_sec != cSeconds)
 {
  if(bTimePM) 
   secDelta = cSeconds - displ_sec;
  else
   secDelta = (60 - cSeconds) - displ_sec;
 }
#endif

  if(displ_r1m != RED[c1Minute]) r1mDelta = RED[c1Minute] - displ_r1m;
  if(displ_g1m != GRN[c1Minute]) g1mDelta = GRN[c1Minute] - displ_g1m;
  if(displ_b1m != BLU[c1Minute]) b1mDelta = BLU[c1Minute] - displ_b1m;

  if(displ_r10m != RED[c10Minute]) r10mDelta = RED[c10Minute] - displ_r10m;
  if(displ_g10m != GRN[c10Minute]) g10mDelta = GRN[c10Minute] - displ_g10m;
  if(displ_b10m != BLU[c10Minute]) b10mDelta = BLU[c10Minute] - displ_b10m;

  if(displ_r1h != RED[c1Hour]) r1hDelta = RED[c1Hour] - displ_r1h;
  if(displ_g1h != GRN[c1Hour]) g1hDelta = GRN[c1Hour] - displ_g1h;
  if(displ_b1h != BLU[c1Hour]) b1hDelta = BLU[c1Hour] - displ_b1h;

  if(displ_r10h != RED[c10Hour]) r10hDelta = RED[c10Hour] - displ_r10h;
  if(displ_g10h != GRN[c10Hour]) g10hDelta = GRN[c10Hour] - displ_g10h; /* 10Hr blue does not need display mode PWM and fade ability */

#ifndef SEC_BLINK
  if(((SPISTAT & 2) == 0) && ((secDelta) || (r1mDelta) || (g1mDelta) || (b1mDelta) || (r10mDelta) || (g10mDelta) || (b10mDelta) || (r1hDelta) || (g1hDelta) || (b1hDelta) || (r10hDelta) || (g10hDelta))) SPIDATA = 0;
  PAVal = ((displ_g10h > cPWM) ? 0x01 : 0) | ((displ_sec > cPWM) ? ((~cColon) & 0x30) : 0x30) | 4;  // (| 4) to provide ground for 10hBLU as light sensor
#else
  if(((SPISTAT & 2) == 0) && ((r1mDelta) || (g1mDelta) || (b1mDelta) || (r10mDelta) || (g10mDelta) || (b10mDelta) || (r1hDelta) || (g1hDelta) || (b1hDelta) || (r10hDelta) || (g10hDelta))) SPIDATA = 0;
  if(bTimePM)    //for DEBUGGING - add code to allow 10Hr Blue to access values > 2
   PAVal = ((displ_g10h > cPWM) ? 0x01 : 0) | (((64 - cSeconds) > cPWM) ? ((~cColon) & 0x30) : 0x30) | 4; //day winding up - colon brightens as minute proceeds
  else                                   //+1 to force colon to light when the minute changes
   PAVal = ((displ_g10h > cPWM) ? 0x01 : 0) | (((cSeconds + 1) > cPWM) ? ((~cColon) & 0x30) : 0x30) | 4;  //day winding down - colon dims as minute proceeds
#endif
  PBVal = (((displ_r10m > cPWM) ? 0x10 : 0) | ((displ_b10m > cPWM) ? 0x08 : 0) | ((displ_r1m > cPWM) ? 0x04 : 0) | ((displ_g1m > cPWM) ? 0x02 : 0) | ((displ_b1m > cPWM) ? 0x01 : 0));
  PCVal = (((displ_b1h > cPWM) ? 0x20 : 0) | ((displ_r1h > cPWM) ? 0x10 : 0) | ((displ_g1h > cPWM) ? 0x08 : 0) | ((displ_r10h > cPWM) ? 0x04 : 0) | ((displ_g10m > cPWM) ? 0x02 : 0));
  if(bGetDate)
  {
bGetDate = 0;
/*
   DI();
   Read_X1226(0xdf, 0x00, 0x30, (unsigned char *)&cI2CBuf[0], 8);
   Buffer2Time((unsigned char *)&cI2CBuf[0]);
   bGetDate = 0; 
   EI();
*/
  }
  if(bBright)
  { // date should be right 'cuz if(bGetDate) was set, the date was re-read at midnight 
   bDoSetClock = 0;
   x = cSeconds;
   for(c = 0; c < 0x0a;)
   {// In ShowDate mode, a second light blast causes SetClock mode
    PAVal = ((bDoSetClock == 2) ? 0x20 : 0) | ((c == 4) ? 0x10 : 0) | ((GRN[c10Month] > cPWM) ? 0x01 : 0x00) | 4; //colon always ON when showing date 
    PBVal = (((RED[c10Day] > cPWM) ? 0x10 : 0) | ((BLU[c10Day] > cPWM) ? 0x08 : 0) | ((RED[c1Day] > cPWM) ? 0x04 : 0) | ((GRN[c1Day] > cPWM) ? 0x02 : 0) | ((BLU[c1Day] > cPWM) ? 0x01 : 0));
    PCVal = (((BLU[c1Month] > cPWM) ? 0x20 : 0) | ((RED[c1Month] > cPWM) ? 0x10 : 0) | ((GRN[c1Month] > cPWM) ? 0x08 : 0) | ((RED[c10Month] > cPWM) ? 0x04 : 0) | ((GRN[c10Day] > cPWM) ? 0x02 : 0));
    if((c >= 5) && (bDoSetClock == 1) && bBright) bDoSetClock = 2;
    if(!bDoSetClock && !bBright) bDoSetClock = 1;
    if(x != cSeconds)
    {
     c++;
     x = cSeconds;
    }
   }
   if(bDoSetClock == 2)
   {
    T0CTL = 0x09;   //0(disable) 0(polar) 001(presc) 001(mode)
    SetTime(0);
   }
  }
 }
 WDTCTL = 0x55;  //should never execute this - if it does, the chip will reboot
 WDTCTL = 0xaa;
 WDTU = 0x00;    //0x46a0 == about 2 seconds, see Z8 errata
 WDTH = 0x46;
 WDTL = 0xa0;
 asm("wdt");
 asm("hlt");
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Enables or disables write to CCR and/or Flash
*  cMode = 0  disable all
*  cMode != 0  enable write for both
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void Set_X1226_Write(char cMode)
{
 unsigned char c;

 if(cMode == 0)
 {
  c = 0;
  Write_X1226(0xde, 0x00, 0x3f, (unsigned char *)&c, 1);
 }
 else
 {
  c = 2;
  Write_X1226(0xde, 0x00, 0x3f, (unsigned char *)&c, 1);
  c = 6;
  Write_X1226(0xde, 0x00, 0x3f, (unsigned char *)&c, 1);
 }
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Address is a UL to support writes to both CCR and Flash
*  address format: 0x00[ccr | flash]aaaa
*  0xde selects ccr, 0x?? selects flash 
*
* X1226 Write mode must be enabled AND disabled by caller (when needed)
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void Write_X1226(unsigned char cTarget, unsigned char cAddrHi, unsigned char cAddrLo, unsigned char *pSrc, unsigned char cLength)
{
 unsigned char c;

 if(((cTarget & 0x01) == 0x00) && (cAddrHi < 0x2)) // the caller really means to be here (write == 0 and address < 0x200)
 {
  I2CCTL = 0x80;  //enable
  //assume it does...  I2C_Reset(); //does this nuke write-enabled in x1226??
  I2CDATA = cTarget;

  I2CCTL = 0xc0;  //enable, start
  while((I2CSTAT & I2C_TDRE) != I2C_TDRE); //wait for it to clock out
  I2CCTL = 0xa0;  // STOP, yet another attempt to reset the chip

  //Device ID
//if( (I2CSTAT & I2C_TDRE) != I2C_TDRE )
// asm("brk");
  I2CDATA = cTarget;
  I2CCTL = 0xc0;  //enable, start
  while((I2CSTAT & I2C_TDRE) != I2C_TDRE); //wait for it to clock out
  do
  {
   c = I2CSTAT & 0x21;
  }while(c == 0);  //wait for ack OR nack
  if((I2CSTAT & I2C_ACK) != I2C_ACK)
	return;

  //Address Hi
//if( (I2CSTAT & I2C_TDRE) != I2C_TDRE )
// asm("brk");
  I2CDATA = cAddrHi;
  while((I2CSTAT & I2C_TDRE) != I2C_TDRE); //wait for it to clock out
  do
  {
   c = I2CSTAT & 0x21;
  }while(c == 0);  //wait for ack OR nack
  //if(I2CSTAT & I2C_RDRF) return;  //something broken
  if((I2CSTAT & I2C_ACK) != I2C_ACK)
	return;

//Address Lo
//if( (I2CSTAT & I2C_TDRE) != I2C_TDRE )
// asm("brk");
  I2CDATA = cAddrLo;
  while((I2CSTAT & I2C_TDRE) != I2C_TDRE); //wait for it to clock out
  do
  {
   c = I2CSTAT & 0x21;
  }while(c == 0);  //wait for ack OR nack
  //if(I2CSTAT & I2C_RDRF) return; //something broken
  if((I2CSTAT & I2C_ACK) != I2C_ACK)
	return;

//  I2CCTL = 0xc0;  //enable, start
  while(cLength--)
  {
   if((I2CSTAT & I2C_TDRE) == I2C_TDRE) 
   {
    //c0=I2CSTAT;
    I2CDATA = *pSrc++;
    //c1=I2CSTAT;
    while((I2CSTAT & I2C_TDRE) != I2C_TDRE); //wait for it to clock out
    do
    {
     c = I2CSTAT & 0x21;
    }while(c == 0);  //wait for ack OR nack
    //if(I2CSTAT & I2C_RDRF) return;  //something broken
    if((I2CSTAT & I2C_ACK) != I2C_ACK)
	return;
   }
  };
//I should care whether the last byte was acked, but plbbt!
  I2CCTL = 0xa0; // IEN, STOP - do I have to clock something out???
  return;
 }
 return;
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Address is 2 bytes to support writes to both CCR and Flash
*  address format: 0x00[ccr | flash]aaaa
*  0xde selects ccr, 0x?? selects flash 
*
* cTarget must contain the address with the READ bit set
*
* returns < 0 for errors
*         == 0 success
* 
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void Read_X1226(unsigned char cTarget, unsigned char cAddrHi, unsigned char cAddrLo, unsigned char *pDest, unsigned char cLength)
{

 if(((cTarget & 0x01) == 0x01) && (cAddrHi < 0x2)) // the caller really means to be here (read == 1 and address < 0x200)
 {
  I2C_Reset();
  I2CDATA = cTarget & 0xfe;
  I2CCTL = 0xc0;  //enable, start
  while((I2CSTAT & I2C_TDRE) != I2C_TDRE); //wait for it to clock out
  asm("nop"); // kill time without wait();
  I2CCTL = 0xa0; //enable, stop
  while((I2CCTL & I2C_STOP) == I2C_STOP);
  I2CDATA = cTarget & 0xfe;   //begin with a fake write to set read address
  I2CCTL = 0xc0;  //enable, start
  while((I2CSTAT & I2C_TDRE) != I2C_TDRE); //wait for it to clock out
  asm("nop"); // kill time without wait();
  if((I2CSTAT & I2C_ACK) != I2C_ACK) return;
asm("nop");
asm("nop");
  I2CDATA = cAddrHi; 
asm("nop");
asm("nop");
 // HANGS UP HERE!!
  while((I2CSTAT & I2C_TDRE) != I2C_TDRE); //wait for it to clock out
  asm("nop"); // kill time without wait();
  if((I2CSTAT & I2C_ACK) != I2C_ACK) return;
  I2CDATA = cAddrLo;
  while((I2CSTAT & I2C_TDRE) != I2C_TDRE); //wait for it to clock out
  asm("nop"); // kill time without wait();
  if((I2CSTAT & I2C_ACK) != I2C_ACK) return;
  I2CCTL = 0xc0; //enable, start
  I2CDATA = cTarget;
  while((I2CSTAT & I2C_TDRE) != I2C_TDRE); //wait for it to clock out
  asm("nop"); // kill time without wait();
  if((I2CSTAT & I2C_ACK) != I2C_ACK) return;
  while(cLength--)
  { 
   while(((I2CSTAT & I2C_RDRF) != I2C_RDRF));
   *pDest++ = I2CDATA;
  }
  //if((I2CSTAT & I2C_RDRF) == I2C_RDRF)
  // *pDest++ = I2CDATA;
  I2CCTL = 0x84; //IEN, NAK - done
  asm("nop"); // kill time without wait();
  I2CCTL = 0xa0; // IEN, STOP
  while((I2CCTL & I2C_STOP) == I2C_STOP);
  if((I2CSTAT & I2C_RDRF) == I2C_RDRF)
  *pDest++ = I2CDATA; 
 }
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Blinks lights and monitors ambient light to collect values to get a new time
*  value and sets the new time 
*
* Time is set 1 digit at a time, while date is done in pairs, and
*  year shows all 4 digits
*
* // allow 24 Hr. time to be set, adjust MIL flag as needed
* //  however, this mode can only be accessed after 1 p.m. (13:00)
*
* Aborts if the current value cycles 5 times without detecting bright light
*
* cMode == 0 set clock
* //cMode == 1 set alarm 1 Mon-Fri - multi-day alarm
* //cMode == 2 set alarm 2 Sat
* //cMode == 3 set alarm 3 Sun
* //cMode == 4 set alarm 4 Mon - individual day alarms override the multi-day alarm
* //cMode == 5 set alarm 5 Tue
* //cMode ==      ...
* //cMode == 8 set alarm 8 Fri
*
 2) set 1M value 0-9        (1M)
 3) set 10M value 0-5       (10M)
 4) set 1H value 0-9        (1H)
 5) IF 1H value < 3 THEN set 10H value 0-1 (10H)
 6) // IF 10H + 1H > 12 THEN mode = 24 Hr.
 7) //set Day of Week 0-6     (1M) displayed as 1-7
 8) set day value 1-31      (1M,10M)
 9) set month value 1-12    (1H,10H)
11) IF Flash 000:001 != oxffff THEN initial year(IY) = Flash 000:001 ELSE IY = 2007
12) set year value IY  (1M, 10M, 1H, 10H)
13) choose Speed-up, Slow-down, or No-change for digital trimmer
16) wait for bright - Commit time!
17) update current time value, cSeconds = 0
18) set clock
19) write year to flash 000:001
20) resume clock operation

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
char SetTime(char cMode)
{
 unsigned char cBuf[8];
 unsigned char PANew;
 unsigned char PBNew;
 unsigned char PCNew;
 unsigned char cValue;
 unsigned char cYearHi;
 unsigned char cRangeLo;
 unsigned char cRangeHi;
 unsigned char cIncValue;
 unsigned char cState;
 unsigned char cIncDec;
 unsigned char cLoops;
 unsigned char cNewTime[0x08] = {0};  //sec  //min  //hr |= 0x20 for PM  //date  //month  //year  //day o'week  //y2k
 unsigned char cActiveMap;
 unsigned char bTick;
 unsigned char cSec;
 char c;

 while(bBright);  //move the light already!
 for(cState = 0; cState < 0x0a; cState++)
 {
  switch(cState)
  {
   case 0: //1 min
    cActiveMap = 1;
    cValue = 0x01;  //start counts at 1, for easy visibility
    cRangeLo = 0x00;
    cRangeHi = 0x09;
    cIncValue = 0x01;
    break;
   case 1: //10 min
    cActiveMap = 2;
    cValue = 0x10;  //start counts at 1, for easy visibility
    cRangeLo = 0x00;
    cRangeHi = 0x50;
    cIncValue = 0x10;
    break;
   case 2: //1 hour
    cActiveMap = 4;
    cValue = 0x01;  //start counts at 1, for easy visibility
    cRangeLo = 0x00;
    cRangeHi = 0x09;
    cIncValue = 0x01;
    break;
   case 3: //10 hour
    if(cNewTime[2] < 3)  
    {
     cActiveMap = 8;
     cValue = 0x10;  //start counts at 1, for easy visibility
     cRangeLo = 0x00;
     cRangeHi = 0x10;
     cIncValue = 0x10;
    }
    else
     continue;
    break;
   case 4:  //AM/PM
    cActiveMap = 0x0f; //segments
    cValue = 0x44;
    cRangeLo = 0x00;
    cRangeHi = 0x44;
    cIncValue = 0x44;
    break;
   case 5:  //Day of Week - X1226 uses 0-6, display as 11-77
   #ifdef USE_DOW
    cActiveMap = 0x03;
    cValue = 0x11;
    cRangeLo = 0x11;
    cRangeHi =0x77;
    cIncValue = 0x11;
   #else
    continue;
   #endif
    break;
   case 6: //day of month
    cActiveMap = 0x03;
    cValue = 0x01;
    cRangeLo = 0x01;
    cRangeHi = 0x31;
    cIncValue = 0x01;
    break;
   case 7: //month
    cActiveMap = 0x0c;
    cValue = 0x01;
    cRangeLo = 0x01;
    cRangeHi = 0x12;
    break;
   case 8: //year
    Read_X1226(0xaf, 0x00, 0x00, (unsigned char *)&cBuf[0], 2);
    if((cBuf[0] == 0xff) || (cBuf[1] == 0xff))  // || (c < 0)
    {
     cYearHi = 0x20;
     cValue = 0x07;
    }
    else
    {
asm("brk");
     cYearHi = cBuf[0];
     cValue = cBuf[1];
    }
    cActiveMap = 15;
    cRangeLo = 0x00;
    cRangeHi = 0x99;
    break;
   case 9: // faster/slower
    cActiveMap = 0x01;
    cValue = 0x08;
    cRangeLo = 0x02;
    cRangeHi = 0x08;
    cIncValue = 0x03;
    break;
  }
  cSec = cSeconds;
  while(cSec == cSeconds);  //wait for the current second to end
  cSec = cSeconds;
  PAVal = 0x30;
  PBVal = 0;
  PCVal = 0;
  bTick = 0;
  T0CTL = 0x89;   //1(enable) 0(polar) 001(presc) 001(mode)
  for(cLoops = 0; cLoops < MAX_SETTIME_LOOPS; ) //loop through the value range 5 times
  {
   if(cSec != cSeconds) // 1 second has elapsed
   {
    bTick++;
    cSec = cSeconds;
   }
   if(bBright)
   {
    PAVal = 0;  // colon ON, everything off
    PBVal = 0;
    PCVal = 0;
    while(cSec == cSeconds);
    break;  //light detected!
   }
   if(!bBright && (bTick >= 2)) //don't inc value if it became bright and the time interval has elapsed!
   {   //since 2 seconds have elapsed, try the next value
    bTick = 0;
    cValue += cIncValue;  //will show assembly code to do this, in case the following doesn't work
    asm("DA R4");
    if(cValue > cRangeHi) //BROKEN if cState == 6. cLoops should increment when cValueHi rolls over BUT not when cValue rolls
    {
     cValue = cRangeLo;  //roll over to 0, or low value
     cLoops++;
     if(cState == 8)
     {
      asm("LDX R0, %FC(RR14)");  //cYearHi++;
      asm("INC R0");
      asm("DA R0");
      asm("LDX %FC(RR14), R0");
      cValue = 0;
     }
    }
   }
   if(cState != 8) cYearHi = cValue;

   PANew = ((GRN[cYearHi >> 4] > cPWM) ? 0x01 : 0) | (cColon ^ 0x20); //alternating full brightness colon
   PBNew = (((RED[cValue >> 4] > cPWM) ? 0x10 : 0) | ((BLU[cValue >> 4] > cPWM) ? 0x08 : 0) | ((RED[cValue & 0x0f] > cPWM) ? 0x04 : 0) | ((GRN[cValue & 0x0f] > cPWM) ? 0x02 : 0) | ((BLU[cValue & 0x0f] > cPWM) ? 0x01 : 0));
   PCNew = (((BLU[cYearHi & 0x0f] > cPWM) ? 0x20 : 0) | ((RED[cYearHi & 0x0f] > cPWM) ? 0x10 : 0) | ((GRN[cYearHi & 0x0f] > cPWM) ? 0x08 : 0) | ((RED[cYearHi >> 4] > cPWM) ? 0x04 : 0) | ((GRN[cValue >> 4] > cPWM) ? 0x02 : 0));

   switch(cActiveMap)
   {
    case 1:  // 1 Min only
     PANew &= 0x30;
     PBNew &= 0x07;
     PCNew &= 0x00;
     break;
    case 2:  // 10 Min only
     PANew &= 0x30;
     PBNew &= 0x18;
     PCNew &= 0x02;
     break;
    case 3:  // 1 and 10 Min
     PANew &= 0x30;
     PBNew &= 0x1f;
     PCNew &= 0x02;
     break;
    case 4:  // 1 Hr only
     PANew &= 0x30;
     PBNew &= 0x00;
     PCNew &= 0x38;
     break;
    case 8:  // 10 Hr only
     PANew &= 0x0b;
     PBNew &= 0x00;
     PCNew &= 0x04;
     break;
    case 0x0c: // 10 Hr and 1 Hr
     PANew &= 0x31;
     PBNew &= 0x00;
     PCNew &= 0x3c;
     break;
    //case 0x0f:  // all - alters nothing
     //PANew &= 0x31;
     //PBNew &= 0x1f;
     //PCNew &= 0x3e;
     //break;
   }
   PAVal = PANew;
   PBVal = PBNew;
   PCVal = PCNew;
  }
  if(cLoops > MAX_SETTIME_LOOPS) return(-1); //C_ABORT
  switch(cState)
  {
   case 0:  // 1M
    cNewTime[1] = cValue;
    break;
   case 1:  // 10M
    cNewTime[1] |= cValue;
    break;
   case 2:  // 1H
    cNewTime[2] = cValue;
    break;
   case 3:  // 10H
    cNewTime[2] |= cValue;
    break;
   case 4:  // AM/PM
    if(cValue == 0) cNewTime[2] |= 0x20;
    break;
   case 5:  // day of week
   #ifdef USE_DOW
    cNewTime[6] = (cValue - 1) & 0x0f;
   #endif
    break;
   case 6:  // day of month
    cNewTime[3] = cValue;
    break;
   case 7:  // month
    cNewTime[4] = cValue;
    break;
   case 8:  // year
    cNewTime[5] = cValue;
    cNewTime[7] = cYearHi;
    break;
   case 9:
    cIncDec = cValue;
    break;
  }
 }
 PAVal = 1;
 PBVal = 2;
 PCVal = 0x0a;
 while(bBright);  //move the light already!
 Set_X1226_Write(3); //non-volatile enable?
 for(;!bBright;)
 {  // track cSeconds in case non-blinking colon is enabled
  PAVal = ((cColon ^ 0x20) | 1);  //alternating full brightness colon
 }
 Write_X1226(0xde, 0x00, 0x30, (unsigned char *)&cNewTime[0], 8);
 Buffer2Time((unsigned char *)&cNewTime[0]); //Update active values
 if(cIncDec != 8)
 {
  Read_X1226(0xdf, 0x00, 0x13, (unsigned char *)&c, 1);
  if(c & 4) c |= 0xf8;
  c = (c & 0xfc) | ((c & 1) << 1) | ((c & 2) >> 1);
  if(cIncDec == 3)
  { if(c > -2) c--; }
  else
  { if(c < 3) c++; }
  c = ((c & 0x80) >> 5) | ((c & 1) << 1) | ((c & 2) >> 1);
  Write_X1226(0xde, 0x00, 0x30, (unsigned char *)&c, 1); //apply speed-up/slow-down
 }
//  7f(s) 7f(m) bf(h) 3f(dy) 1f(mo) ff(yr) 07(dow) 19(cen)
/* 
  cBuf[0] = cNewTime[7];
  cBuf[1] = cNewTime[5];
  Set_X1226_Write(3); //non-volatile write enable, iirc
  X1226_Erase(0, 2); //erase 2 bytes
  Write_X1226(0xaf, 0x00, 0x00, (unsigned char *)&cBuf[0], 2);
*/
 Set_X1226_Write(0);
 return(0);
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Takes a buffer and sets all active time variables
* To eliminate a bit of redundant code
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void Buffer2Time(unsigned char *pData)
{
 unsigned char c;

 //cSeconds = 0;
 c = ((*pData & 0xf0) >> 4);
 cSeconds = c + c + c + c + c + c + c + c + c + c;  // BCD to binary w/o software MUL
 cSeconds += (*pData & 0x0f);
#ifdef SEC_BLINK
 cColon = (((cSeconds & 1) == 1) ? 0x00 : 0x30);
#else
 #ifdef ALTERNATING_COLON
  cColon = 0x10;
 #else
  cColon = 0x30;
 #endif
#endif
 c10Minute = (*(pData + 1) & 0xf0) >> 4;
 c1Minute = *(pData + 1) & 0x0f;
 bTimePM = ((*(pData + 2) & 0x20) == 0x20);
 c10Hour = (*(pData + 2) & 0x10) >> 4;
 c1Hour = (*(pData + 2) & 0x0f);
 c10Month = (*(pData + 4) & 0xf0) >> 4;
 c1Month = (*(pData + 4) & 0x0f);
 c10Day = (*(pData + 3) & 0xf0) >> 4;
 c1Day = (*(pData + 3) & 0x0f);
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Supposedly defined as an I2C bus reset
*  I should look into which condition (Start/Stop) should be used
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void I2C_Reset(void)
{
 I2CCTL = 0; 
 I2CDATA = 0x06; // defined I2C reset
 I2CCTL = 0xa0; 
 while((I2CCTL & I2C_STOP) == I2C_STOP);
 asm("nop"); // kill time without wait();
/* asm("nop");
 asm("nop");
 asm("nop");
 asm("nop");
 asm("nop");*/
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Pushes PxVals to LEDs
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#pragma interrupt
void DisplayISR(void)
{

 PAOUT = PAVal;
 PBOUT = PBVal;
 PCOUT = PCVal;
 if(cPWM == 0x3f)
  cPWM = 0;
 else
  cPWM++;
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#pragma interrupt
void SPI_BRG_IRQ(void)
{
 char cRunSPI = 0;

#ifndef SEC_BLINK
 if(secDelta > 0)
 {
  displ_sec++;
  secDelta--;
  cRunSPI++;
 }
 else
 {
  if(secDelta < 0)
  {
   displ_sec--;
   secDelta++;
   cRunSPI++;
  }
 }
#endif
 if(r1mDelta > 0)
 {
  displ_r1m++;
  r1mDelta--;
  cRunSPI++;
 }
 else
 {
  if(r1mDelta < 0)
  {
   displ_r1m--;
   r1mDelta++;
   cRunSPI++;
  }
 }
 if(g1mDelta > 0)
 {
  displ_g1m++;
  g1mDelta--;
  cRunSPI++;
 }
 else
 {
  if(g1mDelta < 0)
  {
   displ_g1m--;
   g1mDelta++;
   cRunSPI++;
  }
 }
 if(b1mDelta > 0)
 {
  displ_b1m++;
  b1mDelta--;
  cRunSPI++;
 }
 else
 {
  if(b1mDelta < 0)
  {
   displ_b1m--;
   b1mDelta++;
   cRunSPI++;
  }
 }

 if(r10mDelta > 0)
 {
  displ_r10m++;
  r10mDelta--;
  cRunSPI++;
 }
 else
 {
  if(r10mDelta < 0)
  {
   displ_r10m--;
   r10mDelta++;
   cRunSPI++;
  }
 }
 if(g10mDelta > 0)
 {
  displ_g10m++;
  g10mDelta--;
  cRunSPI++;
 }
 else
 {
  if(g10mDelta < 0)
  {
   displ_g10m--;
   g10mDelta++;
   cRunSPI++;
  }
 }
 if(b10mDelta > 0)
 {
  displ_b10m++;
  b10mDelta--;
  cRunSPI++;
 }
 else
 {
  if(b10mDelta < 0)
  {
   displ_b10m--;
   b10mDelta++;
   cRunSPI++;
  }
 }

 if(r1hDelta > 0)
 {
  displ_r1h++;
  r1hDelta--;
  cRunSPI++;
 }
 else
 {
  if(r1hDelta < 0)
  {
   displ_r1h--;
   r1hDelta++;
   cRunSPI++;
  }
 }
 if(g1hDelta > 0)
 {
  displ_g1h++;
  g1hDelta--;
  cRunSPI++;
 }
 else
 {
  if(g1hDelta < 0)
  {
   displ_g1h--;
   g1hDelta++;
   cRunSPI++;
  }
 }
 if(b1hDelta > 0)
 {
  displ_b1h++;
  b1hDelta--;
  cRunSPI++;
 }
 else
 {
  if(b1hDelta < 0)
  {
   displ_b1h--;
   b1hDelta++;
   cRunSPI++;
  }
 }

 if(r10hDelta > 0)
 {
  displ_r10h++;
  r10hDelta--;
  cRunSPI++;
 }
 else
 {
  if(r10hDelta < 0)
  {
   displ_r10h--;
   r10hDelta++;
   cRunSPI++;
  }
 }
 if(g10hDelta > 0)
 {
  displ_g10h++;
  g10hDelta--;
  cRunSPI++;
 }
 else
 {
  if(g10hDelta < 0)
  {
   displ_g10h--;
   g10hDelta++;
   cRunSPI++;
  }
 }
/*
 if(b10hDelta > 0)
 {
  displ_b10h++;
  b10hDelta--;
  cRunSPI++;
 }
 else
 {
  if(b10hDelta < 0)
  {
   displ_b10h--;
   b10hDelta++;
   cRunSPI++;
  }
 }*/

 SPISTAT = 0x80;
 if(cRunSPI) SPIDATA = 0;
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Second info comes from RTC, adjusts current time
* selects colon mode
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#pragma interrupt
void TickIRQ(void)
{

 if(IRQES)
 {
  cSeconds++;
  if(cSeconds >= 60)
  {
   cSeconds = 0;
   c1Minute++;
   if(c1Minute >= 0x0a)
   {
    c1Minute = 0;
    c10Minute++;
    if(c10Minute >= 6)
    {
     c10Minute = 0;
     c1Hour++;
     if(c10Hour == 0)
     {
      if(c1Hour >= 0x0a)
      {
       c1Hour = 0;
       c10Hour = 1;
      }
     }
     else
     {
      if(c1Hour >= 3) //Unless 24hr. option is selected
      {
       c1Hour = 1;
       c10Hour = 0;
      }
      if(c1Hour == 2)
      {
       bTimePM ^= 1;
       bGetDate = 1;
      }
     }
    }
   }
  }
 }
 #ifdef ALTERNATING_COLON
 cColon ^= 0x30;
 #endif
 IRQES ^= 2;
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* monitors timer 1 and LED to gauge brightness
*  See http://www.hackaday.com/??? for details
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#pragma interrupt
void T1IRQ(void)
{
 unsigned char c;

 bBright = 0;            //Got here first, it must be dark
 c = (PAVal & 0x37) | 4; //pre-calc temporary PAVal to minimize DI() time
 DI();
 PADD = 0x02;            //config port for normal operation
 PAOUT = c;              //charge LED
 PADD = 0x06;            //config port for reading
 EI();
 T1CTL = 0xB8; //1(enable) 0(polar) 111(presc) 000(mode)
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* monitors timer 1 and LED to gauge brightness
* only executes if LED discharges due to bright light
* 
* user polls bBright for some period of time to determine if the
*  environment became brighter during that period of time
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#pragma interrupt
void BrightIRQ(void)
{
 unsigned char c;

 if((PAIN & 4) == 0)  //something is causing spurious interrupts
 {
  bBright = 1;  //set the It's_Bright flag 
  c = (PAVal & 0x37) | 4;     //pre-calc temporary PAVal to minimize DI() time
  DI();
  PADD = 0x02;                //config port for normal operation
  PAOUT = c;                  //charge LED
  PADD = 0x06;                //config port for reading
  EI();
  T1CTL = 0xB8; //1(enable) 0(polar) 111(presc) 000(mode)
 }
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Sets up all GPIO
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void init(void)
{
 DI();
 I2CDIAG = 0;
 I2CCTL = 0;

 PAAF = 0xc0; 
 PADD = 0x02;  //a6, a7 i2c
 PAOC = 0xf1;  // USING open collectors with pull-ups, because the board is built with 2n3904
 PAHDE = 0x3c; //  instead of BSS138. 
 PAOUT = 9; //All on
 PASMRE = 0;
 PAAF = 0xc0; 
 PAPUE = 0xf3;  //I2C REQUIRES Pull-ups!!!

 I2CDIAG = 0;
 I2CBRH = 0;
 //I2CBRL = 0xd; //about 384 kbps //I2CBRL = 0x19; // 200 kbps 
 I2CBRL = 0x32; // 100 kbps
 //I2CBRL = 0x64; //50 KHz
 //I2CBRH = 0x01; //10 KHz
 //I2CBRL = 0xf4; //10 KHz

 SPICTL = 0xA3;
 SPIBRH = 0x13; //using SPI for coalesce timer
 SPIBRL = 0x12;

 PBDD = 0;
 PBAF = 0;
 PBOC = 0x1f;
 PBHDE = 0x0;
 PBOUT = 0x1f; //All on
 PBSMRE = 0;
 PBPUE = 0x1f;

 PCDD = 0;
 PCAF = 0;
 PCOC = 0x3f;
 PCHDE = 0;
 PCOUT = 0x3e; //All on
 PCSMRE = 0;
 PCPUE = 0x3f;

 SET_VECTOR(TIMER0, DisplayISR); //displayIRQ
 SET_VECTOR(P1AD, TickIRQ); //tickIRQ
 SET_VECTOR(TIMER1, T1IRQ);
 SET_VECTOR(P2AD, BrightIRQ);
 SET_VECTOR(SPI, SPI_BRG_IRQ);

 IRQES = 2;
 IRQ0ENH = 0x62; // timer0 - HIGH priority, timer1 - normal, SPI normal
 IRQ0ENL = 0x20; // timer0  
 IRQ1ENH = 0x06; // A1, A2 IRQ - normal priority
 IRQ1ENL = 0x00;
 IRQ2ENH = 0;
 IRQ2ENL = 0;
 //clear interrupts
 IRQ0 &= 0;
 IRQ1 &= 0;
 IRQ2 &= 0;

 T0CTL = 0x09;   //0(disable) 0(polar) 001(presc) 001(mode)
 T0RH = 0x05;    //120 times/sec * 64 divisions
 T0RL = 0x16;
 T0H = 0;
 T0L = 1;
 //T1 not initialized here - Lamptest would alter the settings
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* turns everything off after 2 seconds (turned on during init)
//was in main()
 Lamptest();  //lamptest trashes T1 settings
 T1CTL = 0x38; //0(disable) 0(polar) 111(presc) 000(mode)
 T1RH = 0x7a;  
 T1RL = 0x13;  // .200 sec.
 T1H = 0;
 T1L = 1; 


void Lamptest(void)
{
 char C;
 char cTime;

 for(cTime = 0; cTime < 0x14; cTime++)
 {
  T1CTL = 0x38;  //0(disable) 0(polar) 111(presc) 000(mode)
  T1RH = 0x3d;
  T1RL = 0x0a;   // .1 Sec.
  T1H = 0;
  T1L = 1;
  IRQ0 &= 0xbf;
  T1CTL = 0xb8;  //1(enable) 0(polar) 111(presc) 000(mode)  
  do
  {
   C = (IRQ0 & 0x40);
  }while(C == 0); 
 };
 T1CTL = 0x38;  //0(disable) 0(polar) 111(presc) 000(mode)
 PAOUT = 0x30; //All off
 PBOUT = 0;
 PCOUT = 0;
}
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

