/*
 * Program to run the Grantronics Christmas Star.
 * Program is Copyright (c) Grantronics Pty Ltd 1998.
 * Permission is hereby granted for unrestricted personal
 * use. For commercial use of this software, except for
 * sale "as is" in a kit, please contact Grantronics Pty Ltd.
 *   info@grantronics.com.au

 * Micro/C command line: d:\mc\cc51 star.c -I -L -P -Z M=T

    Last change:  LG   18 Oct 1998    2:13 pm
 */

 /* This software was written for the Dunfield Development Systems Micro/C
    compiler. See our Web site for a description of Micro/C.
	  http://www.grantronics.com.au */

#include <8051bit.h>	/* 8051 bit manipulation macros */
#include <8051reg.h>	/* 8051 SFR definitions */

#define UCHAR unsigned char

#define ROW1 P1.0
#define ROW2 P1.1
#define ROW3 P1.2
#define ROW4 P1.3
#define ROW5 P1.4
#define ROW6 P1.5

#define COL1 P1.6
#define COL2 P1.7
#define COL3 P3.7
#define COL4 P3.3
#define COL5 P3.2

/****************************************************************************/
/* set a bit to turn on a LED, bit 0 of first byte = led01, 6 bits per byte */
register UCHAR LEDs[5];
register UCHAR *TblPtr;		/* pointer into control data table */
register UCHAR LastDelay;	/* last delay value */
register UCHAR LoopCnt;		/* loop counter */
register UCHAR *LoopStart;	/* pointer to loop start */

UCHAR BitMasks[8] = {1,2,4,8,16,32,64,128};

/****************************************************************************/
LedOn(UCHAR led)
{
	LEDs[led/6] |= BitMasks[led%6];
}

/****************************************************************************/
LedOff(UCHAR led)
{
	LEDs[led/6] &= ~(BitMasks[led%6]);
}

/****************************************************************************/
LedsOff()
{
	UCHAR i;

	for ( i=0; i<5; i++)
	{
		LEDs[i] = 0;
	}
}

/****************************************************************************/
void MuxDly(void)
{	/* time for each LED column, about 2ms */
	UCHAR dly;
	for ( dly=50; dly; dly--)
		;
}

/****************************************************************************/
void Mux(void)
{	/* do 1 multiplex cycle, about 10ms */

   	P1 = (P1 & 0xc0) | ~LEDs[0];
    clrbit(COL1);	/* on */
   	MuxDly();
    setbit(COL1);	/* off */
   	P1 = (P1 & 0xc0) | ~LEDs[1];
    clrbit(COL2);	/* on */
   	MuxDly();
    setbit(COL2);	/* off */
   	P1 = (P1 & 0xc0) | ~LEDs[2];
    clrbit(COL3);	/* on */
   	MuxDly();
    setbit(COL3);	/* off */
   	P1 = (P1 & 0xc0) | ~LEDs[3];
    clrbit(COL4);	/* on */
   	MuxDly();
    setbit(COL4);	/* off */
   	P1 = (P1 & 0xc0) | ~LEDs[4];
    clrbit(COL5);	/* on */
   	MuxDly();
    setbit(COL5);	/* off */
}

/****************************************************************************/
#define LOOPCNT 0x40+	/* define loop start, 0x41..0x4f = 1..15, count = value - 0x40 */
#define LOOP	0x40	/* back to loop start */
#define ALLON   0xfd	/* all LEDs on */
#define ALLOFF  0xfe
#define END     0xff
#define DELAY   0x80+	/* DELAY 0: use last delay value, max = 63 */
#define	OFF 	0x20+
UCHAR data[] = {

				/* Random on */
				ALLOFF,
				5,DELAY 10,
                24,DELAY 0,
				13,DELAY 0,
				2,DELAY 0,
				21,DELAY 0,
				10,DELAY 0,
				29,DELAY 0,
				18,DELAY 0,
				7,DELAY 0,
				26,DELAY 0,
				15,DELAY 0,
				4,DELAY 0,
				23,DELAY 0,
				12,DELAY 0,
				1,DELAY 0,
				20,DELAY 0,
				9,DELAY 0,
				28,DELAY 0,
				17,DELAY 0,
				6,DELAY 0,
				25,DELAY 0,
				14,DELAY 0,
				3,DELAY 0,
				22,DELAY 0,
				11,DELAY 0,
				30,DELAY 0,
				19,DELAY 0,
				8,DELAY 0,
				27,DELAY 0,
				16,DELAY 50,
				/* now all on */

                /* flashes */
				LOOPCNT 3,
				ALLON,DELAY 50,
				ALLOFF,DELAY 0,
				LOOP,
				/* now all off */

				/* point to point */
				LOOPCNT 3,
				/* on */
				1,7,13,19,25,DELAY 20,
				2,8,14,20,26,DELAY 0,
				3,9,15,21,27,DELAY 0,
				4,10,16,22,28,DELAY 0,
				5,11,17,23,29,DELAY 0,
				6,12,18,24,30,DELAY 0,
				DELAY 50,
				/* off */
                OFF 1,OFF 7,OFF 13,OFF 19,OFF 25,DELAY 20,
                OFF 2,OFF 8,OFF 14,OFF 20,OFF 26,DELAY 0,
                OFF 3,OFF 9,OFF 15,OFF 21,OFF 27,DELAY 0,
                OFF 4,OFF 10,OFF 16,OFF 22,OFF 28,DELAY 0,
                OFF 5,OFF 11,OFF 17,OFF 23,OFF 29,DELAY 0,
                OFF 6,OFF 12,OFF 18,OFF 24,OFF 30,DELAY 0,
				DELAY 50,
				LOOP,

				/* clockwise rotating bar */
				LOOPCNT 5,
				ALLOFF,
				1,2,3,4,DELAY 20,
				ALLOFF,
				7,8,9,10,DELAY 0,
				ALLOFF,
				13,14,15,16,DELAY 0,
				ALLOFF,
				19,20,21,22,DELAY 0,
				ALLOFF,
				25,26,27,28,DELAY 0,
				LOOP,

				/* anti-clockwise rotating bar */
				LOOPCNT 5,
				ALLOFF,
				28,29,30,1,DELAY 20,
				ALLOFF,
				22,23,24,25,DELAY 0,
				ALLOFF,
				16,17,18,19,DELAY 0,
				ALLOFF,
				10,11,12,13,DELAY 0,
				ALLOFF,
				4,5,6,7,DELAY 0,
				LOOP,

				LOOPCNT 2,
				/* pour down */
                ALLOFF,1,DELAY 10,
                2,30,DELAY 0,
                3,29,DELAY 0,
                4,28,DELAY 0,
                5,27,DELAY 0,
                6,26,DELAY 0,
                7,25,DELAY 0,
                8,24,DELAY 0,
                9,23,DELAY 0,
                10,22,DELAY 0,
                11,21,DELAY 0,
                12,20,DELAY 0,
                13,19,DELAY 0,
                14,18,DELAY 0,
                15,17,DELAY 0,
                16,DELAY 40,

				/* drain from top (assumes all on) */
                OFF 1,DELAY 10,
                OFF 2,OFF 30,DELAY 0,
                OFF 3,OFF 29,DELAY 0,
                OFF 4,OFF 28,DELAY 0,
                OFF 5,OFF 27,DELAY 0,
                OFF 6,OFF 26,DELAY 0,
                OFF 7,OFF 25,DELAY 0,
                OFF 8,OFF 24,DELAY 0,
                OFF 9,OFF 23,DELAY 0,
                OFF 10,OFF 22,DELAY 0,
                OFF 11,OFF 21,DELAY 0,
                OFF 12,OFF 20,DELAY 0,
                OFF 13,OFF 19,DELAY 0,
                OFF 14,OFF 18,DELAY 0,
                OFF 15,OFF 17,DELAY 0,
                OFF 16,DELAY 40,
				LOOP,
				/* now all off */

				LOOPCNT 2,
				/* concentric out */
//				ALLOFF,
				4,DELAY	20,
				10,DELAY 0,
				16,DELAY 0,
				22,DELAY 0,
				28,DELAY 0,
				5,9,DELAY 0,
				11,15,DELAY 0,
				17,21,DELAY 0,
				23,27,DELAY 0,
				29,3,DELAY 0,
				6,8,DELAY 0,
				12,14,DELAY 0,
				18,20,DELAY 0,
				24,26,DELAY 0,
				30,2,DELAY 0,
				7,DELAY 0,
				13,DELAY 0,
				19,DELAY 0,
				25,DELAY 0,
				1,DELAY 40,
				/* now all on */

				/* Random off */
				OFF 4,DELAY 10,
				OFF 23,DELAY 0,
				OFF 12,DELAY 0,
				OFF 1,DELAY 0,
				OFF 20,DELAY 0,
				OFF 9,DELAY 0,
				OFF 28,DELAY 0,
				OFF 17,DELAY 0,
				OFF 6,DELAY 0,
				OFF 25,DELAY 0,
				OFF 14,DELAY 0,
				OFF 3,DELAY 0,
				OFF 22,DELAY 0,
				OFF 11,DELAY 0,
				OFF 30,DELAY 0,
				OFF 19,DELAY 0,
				OFF 8,DELAY 0,
				OFF 27,DELAY 0,
				OFF 16,DELAY 0,
				OFF 5,DELAY 0,
				OFF 24,DELAY 0,
				OFF 13,DELAY 0,
				OFF 2,DELAY 0,
				OFF 21,DELAY 0,
				OFF 10,DELAY 0,
				OFF 29,DELAY 0,
				OFF 18,DELAY 0,
				OFF 7,DELAY 0,
				OFF 26,DELAY 0,
				OFF 15,DELAY 0,
				/* now all off */
				LOOP,

				/* light chaser or worm */
//                ALLOFF,
				LOOPCNT 3,
				OFF 28,1,DELAY 5,
				OFF 29,2,DELAY 0,
				OFF 30,3,DELAY 0,
				OFF 1,4,DELAY 0,
				OFF 2,5,DELAY 0,
				OFF 3,6,DELAY 0,
				OFF 4,7,DELAY 0,
				OFF 5,8,DELAY 0,
				OFF 6,9,DELAY 0,
				OFF 7,10,DELAY 0,
				OFF 8,11,DELAY 0,
				OFF 9,12,DELAY 0,
				OFF 10,13,DELAY 0,
				OFF 11,14,DELAY 0,
				OFF 12,15,DELAY 0,
				OFF 13,16,DELAY 0,
				OFF 14,17,DELAY 0,
				OFF 15,18,DELAY 0,
				OFF 16,19,DELAY 0,
				OFF 17,20,DELAY 0,
				OFF 18,21,DELAY 0,
				OFF 19,22,DELAY 0,
				OFF 20,23,DELAY 0,
				OFF 21,24,DELAY 0,
				OFF 22,25,DELAY 0,
				OFF 23,26,DELAY 0,
				OFF 24,27,DELAY 0,
				OFF 25,28,DELAY 0,
				OFF 26,29,DELAY 0,
				OFF 27,30,DELAY 0,
				LOOP,
				OFF 28,1,DELAY 0,
				OFF 29,DELAY 0,
				OFF 30,DELAY 0,
				OFF 1,DELAY 40,
				/* now all off */

				/* expanding star */
//				LOOPCNT 2,
//				ALLOFF,
//				DELAY 40,
				4,10,16,22,28, DELAY 40,
				3,5,9,11,15,17,21,23,27,29, DELAY 0,
				2,6,8,12,14,18,20,24,26,30, DELAY 0,
				1,7,13,19,25, DELAY 60,
//				LOOP,

                /* flashes */
				LOOPCNT 2,
				ALLOFF,DELAY 50,
				ALLON,DELAY 0,
				LOOP,

				/* contracting star */
//				LOOPCNT 3,
//				ALLOFF,
//				DELAY 40,
				OFF 1,OFF 7,OFF 13,OFF 19,OFF 25, DELAY 40,
				OFF 2,OFF 6,OFF 8,OFF 12,OFF 14,OFF 18,OFF 20,OFF 24,OFF 26,OFF 30, DELAY 0,
				OFF 3,OFF 5,OFF 9,OFF 11,OFF 15,OFF 17,OFF 21,OFF 23,OFF 27,OFF 29, DELAY 0,
				OFF 4,OFF 10,OFF 16,OFF 22,OFF 28, DELAY 60,
//				LOOP,

                /* flashes */
				LOOPCNT 2,
				ALLON,DELAY 50,
				ALLOFF,DELAY 0,
				LOOP,
				/* now all off */
#if 0
				/* inch worm */
                ALLOFF,
//				LOOPCNT 2,
				1,DELAY 50,			/* pause on 1 */
				2,DELAY	10,
				3,DELAY 0,
				4,DELAY 0,
				OFF 1,DELAY 0,
				OFF 2,DELAY 0,
				OFF 3,DELAY 50,		/* pause on 4 */
				5,DELAY	10,
				6,DELAY 0,
				7,DELAY 0,
				OFF 4,DELAY 0,
				OFF 5,DELAY 0,
				OFF 6,DELAY 50,		/* pause on 7 */
				8,DELAY	10,
				9,DELAY 0,
				10,DELAY 0,
				OFF 7,DELAY 0,
				OFF 8,DELAY 0,
				OFF 9,DELAY 50,		/* pause on 10 */
				11,DELAY 10,
				12,DELAY 0,
				13,DELAY 0,
				OFF 10,DELAY 0,
				OFF 11,DELAY 0,
				OFF 12,DELAY 50,	/* pause on 13 */
				14,DELAY 10,
				15,DELAY 0,
				16,DELAY 0,
				OFF 13,DELAY 0,
				OFF 14,DELAY 0,
				OFF 15,DELAY 50,	/* pause on 16 */
				17,DELAY 10,
				18,DELAY 0,
				19,DELAY 0,
				OFF 16,DELAY 0,
				OFF 17,DELAY 0,
				OFF 18,DELAY 50,	/* pause on 19 */
				20,DELAY 10,
				21,DELAY 0,
				22,DELAY 0,
				OFF 19,DELAY 0,
				OFF 20,DELAY 0,
				OFF 21,DELAY 50,	/* pause on 22 */
				23,DELAY 10,
				24,DELAY 0,
				25,DELAY 0,
				OFF 22,DELAY 0,
				OFF 23,DELAY 0,
				OFF 24,DELAY 50,	/* pause on 25 */
				26,DELAY 10,
				27,DELAY 0,
				28,DELAY 0,
				OFF 25,DELAY 0,
				OFF 26,DELAY 0,
				OFF 27,DELAY 50,	/* pause on 28 */
				29,DELAY 10,
				30,DELAY 0,
				01,DELAY 0,
				OFF 28,DELAY 0,
				OFF 29,DELAY 0,
				OFF 30,DELAY 0,
//				LOOP,
#endif
				/* now all off */
				/* clockwise rotating point */
				LOOPCNT 5,
				28,29,30,1,2,3,4,DELAY 20,
				ALLOFF,
				4,5,6,7,8,9,10,DELAY 0,
				ALLOFF,
				10,11,12,13,14,15,16,DELAY 0,
				ALLOFF,
				16,17,18,19,20,21,22,DELAY 0,
				ALLOFF,
				22,23,24,25,26,27,28,DELAY 0,
				ALLOFF,
				LOOP,
				/* now all off */

				/* anti-clockwise rotating point */
				LOOPCNT 5,
				28,29,30,1,2,3,4,DELAY 20,
				ALLOFF,
				22,23,24,25,26,27,28,DELAY 0,
				ALLOFF,
				16,17,18,19,20,21,22,DELAY 0,
				ALLOFF,
				10,11,12,13,14,15,16,DELAY 0,
				ALLOFF,
				4,5,6,7,8,9,10,DELAY 0,
				ALLOFF,
				LOOP,
				/* now all off */

				/* pour up */
//				ALLOFF,
				16,DELAY 10,
                15,17,DELAY 0,
                14,18,DELAY 0,
                13,19,DELAY 0,
                12,20,DELAY 0,
                11,21,DELAY 0,
                10,22,DELAY 0,
                9,23,DELAY 0,
                8,24,DELAY 0,
                7,25,DELAY 0,
                6,26,DELAY 0,
                5,27,DELAY 0,
                4,28,DELAY 0,
                3,29,DELAY 0,
                2,30,DELAY 0,
				1,DELAY 60,
				/* now all on */

                /* flashes */
				LOOPCNT 2,
				ALLOFF,DELAY 50,
				ALLON,DELAY 0,
				LOOP,

				/* drain from bottom (assumes all on) */
				OFF 16,DELAY 10,
                OFF 15,OFF 17,DELAY 0,
                OFF 14,OFF 18,DELAY 0,
                OFF 13,OFF 19,DELAY 0,
                OFF 12,OFF 20,DELAY 0,
                OFF 11,OFF 21,DELAY 0,
                OFF 10,OFF 22,DELAY 0,
                OFF 9,OFF 23,DELAY 0,
                OFF 8,OFF 24,DELAY 0,
                OFF 7,OFF 25,DELAY 0,
                OFF 6,OFF 26,DELAY 0,
                OFF 5,OFF 27,DELAY 0,
                OFF 4,OFF 28,DELAY 0,
                OFF 3,OFF 29,DELAY 0,
                OFF 2,OFF 30,DELAY 0,
				OFF 1,DELAY 60,
				/* now all off */

                /* walking bar */
				LOOPCNT 5,
                ALLOFF,1,2,3,4,DELAY 20,
				ALLOFF,4,5,6,7,DELAY 0,
				ALLOFF,7,8,9,10,DELAY 0,
				ALLOFF,10,11,12,13,DELAY 0,
				ALLOFF,13,14,15,16,DELAY 0,
				ALLOFF,16,17,18,19,DELAY 0,
                ALLOFF,19,20,21,22,DELAY 0,
                ALLOFF,22,23,24,25,DELAY 0,
                ALLOFF,25,26,27,28,DELAY 0,
                ALLOFF,28,29,30,1,DELAY 0,
				LOOP,

				/* 5 point light chaser */
				/* forward */
				LOOPCNT 5,
                ALLOFF,1,7,13,19,25,DELAY 10,
                ALLOFF,2,8,14,20,26,DELAY 0,
                ALLOFF,3,9,15,21,27,DELAY 0,
                ALLOFF,4,10,16,22,28,DELAY 0,
                ALLOFF,5,11,17,23,29,DELAY 0,
                ALLOFF,6,12,18,24,30,DELAY 0,
				LOOP,
                ALLOFF,1,7,13,19,25,DELAY 20,
				/* backward */
				LOOPCNT 5,
                ALLOFF,6,12,18,24,30,DELAY 10,
                ALLOFF,5,11,17,23,29,DELAY 0,
                ALLOFF,4,10,16,22,28,DELAY 0,
                ALLOFF,3,9,15,21,27,DELAY 0,
                ALLOFF,2,8,14,20,26,DELAY 0,
                ALLOFF,1,7,13,19,25,DELAY 0,
				LOOP,

				/* end of sequence */
//				ALLOFF,
//				DELAY 63,

				END };


/* byte = 0 -> end of data block of leds to turn on/off */
/* byte = 1..30 -> led number to turn on */
/* byte = (1..30)+32 -> led number to turn off */
/* byte = 0x80..0xf8 -> repeat count of byte+0x80 (delay time, 1-120, 0=last delay) */
/* byte = 0xfd -> all leds on */
/* byte = 0xfe -> all leds off */
/* byte = 0xff -> end of sequence, go back to start */

/****************************************************************************/
/****************************************************************************/
main()
{
	static register UCHAR c;	/* byte from data table */
	static register UCHAR i;	/* loop counter */

    LedsOff();		/* clear LEDs array */
	LastDelay = 10;

	TblPtr = data;	/* start reading from the table */
	while ( 1 )
	{	/* main data processing loop */
    	c = *(UCHAR*)TblPtr++;	/* force pointer to access code memory */

		/* test for specific values first */
		if ( !c )
			continue;	/* ignore byte = 0 */

		if ( c == LOOP )
        {
			if ( --LoopCnt )
    		    TblPtr = LoopStart;
			continue;
		}

		if ( c == ALLON )
		{
			for ( i=0; i<5; i++)
			{
                LEDs[i] = 0x3f;		/* only low 6 bits used */
			}
			continue;
		}

		if ( c == ALLOFF )
		{
			for ( i=0; i<5; i++)
			{
                LEDs[i] = 0x00;		/* only low 6 bits used */
			}
			continue;
		}

		if ( c == END )
        {
		    TblPtr = data;
			continue;
		}

		/* now test for block values */
    	if ( !(c & 0xe0) )
    	{	/* turn a LED on */
    		LedOn(c-1);
    		continue;
    	}
    	if ( !(c & 0xc0) )
    	{	/* turn a LED off */
    		LedOff((c&0x1f)-1);
    		continue;
    	}

    	if ( (c & 0xc0) == 0x40 )
    	{	/* loop start */
			LoopCnt = c - 0x40;
            LoopStart = TblPtr;		/* TblPtr already updated */
    		continue;
    	}

		if ( c & 0x80 )
		{	/* must be delay count */
			/* if the delay is 0 (0x80), use last known delay value */
			if ( c != 0x80 )
    			LastDelay = c&0x7f;
			for ( i=LastDelay; i; i--)
			{
    			Mux();	/* do a mux cycle */
			}
			continue;
		}
	}	/* end of main loop */
}

/****************************************************************************/

