/**
 *	Scrolls a message across alphanumeric displays. Written for the 16c77
 *	series, but ports can be reassigned for smaller microcontrollers. Should support
 *	at least 8 characters.
 *  Copyright (2003) William Dubel 04.22.2002
 */
#include <sys.h>
#include <pic.h>

//__CONFIG(HS | PWRTE | UNPROTECT);  // HS Osc, NO wdt, NO PWRTE, NO BOR, NO code protection


#define	XTAL	10000000		// crystal freq
#define	TDIG	8				// total digits

#define RDELAY	550				// roll delay
#define MLENGTH	66				// message length

// Mostly the same order as ASCII, except offset by 32
static const unsigned short chars[60] =
{
	0x0000, 0x4006, 0x090B, 0x3FC0, 0x254D, 0x08A5, 0x3FC0, 0x0040,		/*  !"#$%&' */
	0x0280, 0x2800, 0x3FC0, 0x1540, 0x0008, 0x1100, 0x4000, 0x0880,		/* ()*+_-./ */
	0x08BF, 0x0086, 0x090B, 0x018D, 0x1126, 0x112D, 0x113D, 0x0881,		/* 01234567 */
	0x113F, 0x1127, 0x0440,	0x0440,	0x1540,	0x1108,	0x0280,	0x4523,		/* 89:;<=>? */
	0x3FC0,	0x1137, 0x054F, 0x0039, 0x044F, 0x1039, 0x1031,	0x013D,		/* @ABCDEFG */
	0x1136, 0x0449, 0x001E, 0x12B0, 0x0038, 0x20B6, 0x2236,	0x003F,		/* HIJKLMNO */
	0x1133, 0x023F, 0x1333, 0x210D, 0x0441, 0x003E, 0x08B0,	0x0A36,		/* PQRSTUVW */
	0x2A80, 0x2480, 0x0889, 0x7FFF						/* XYZ#     */
};

static const unsigned char message[MLENGTH] =
	 "THE QUICK BROWN FOX JUMPED OVER THE LAZY DOG 1234567890 TIMES *** ";
	//12345678901234567890123456789012345678901234567890123456789012345678901234567901234567890123456789

static unsigned short digits[TDIG] = { 0, 0, 0, 0};		// all digits off
static char current = 0;								// current digit being refreshed
unsigned char fstate = 0;								// flasher timer
unsigned int tcount = 0;								// schedule count

void rollDigits()
{
	char d;
	for (d = 0; d < TDIG; d++)
	{
		digits[d] = digits[d+1];
	}
}

void placeDigit(char digit, char pos)
{
	digit -= 32;		// offset for ascii
	digits[pos] = chars[digit];
}

void addDigit(char digit)
{
	rollDigits();
	placeDigit(digit, TDIG-1);
}

void refreshDigits()
{
	if (++current>(TDIG-1)) current = 0;

	PORTB = 0;					// turn off while we switch active digit
	PORTD = 0;
	PORTE = current;
	PORTB = digits[current];	// lower half corresponds to segments a - h
	PORTD = digits[current]>>8;	// upper half is segments j - DP

	CLRWDT();
}

void delay(long t)	// delays t units
{
   do
   {
     unsigned int a;
	 for (a = 0; a<100; a++);
   }
   while(--t);
   CLRWDT();
}

void initPic()
{
	// Scheduler setup
	T0CS = 0;		// 1 select internal clock; 0 for instruction clock
	T0IE = 1;		// Enable interrupt on TMR0 overflow
	GIE = 1;		// Global interrupt enable

	// Port I/O definition masks
	TRISA = 0x0F;	// top four enabled (so RA5 is enabled)
   	TRISB = 0x00;	// all outputs enabled
	//TRISC = 0xF0;	// bottom 4 outputs enabled
	TRISD = 0x00;	// all output enabled
	TRISE = 0x00;	// all outputs

	// Port Initialization
	PORTB = 0x00;		// Init input port to 0
	//PORTC = 0x0F;
	PORTD = 0x00;
	PORTE = 0x00;
}

void task_200ms()
{
	RA5=fstate++%2;		// toggle RA5 flasher
}

void task_3ms()
{
	refreshDigits();
}

static void interrupt schedule(void)			// schedules different timed tasks
{
	if(T0IF)				// timer loops and interrupt on zero
	{
		T0IF = 0;			// Clear interrupt flag
		if (tcount%1==0)
		{
							//do realtime task
		}
		if (tcount%32==0)
		{
			task_3ms();		//do high priority task
		}
		if (tcount%2048==0)
		{
			task_200ms();	//do normal task
		}
		tcount++;			// increment schedule clock
	}
	// check for other interrupts
	T0IF = 0;		// Clear interrupt flag
}

void main()
{
	initPic();

	while (1)
	{
		unsigned int j;
		for (j = 0; j<MLENGTH; j++)
		{
			addDigit(message[j]);
			delay(RDELAY);
		}
	}
}
