/*
	SC1TIM0.C

	CM71xx/Z1 and CM72xx/Z2

	This program uses the periodic interrupt by timer0 to modulate the output
	to LEDs D2, D3, D4 and D5 by controlling the on/off time to the LEDs.
*/

#define SIZE 1024        
void set_timer0( unsigned int loadvalue );
void set_dutycycle( float dutycycle );

char pattern[SIZE];		// to store the data pattern to be output
float logcycle;
float deltacycle;
unsigned int datacount;
char timecount;
char *TheAddress;

main(){
	datacount = SIZE;
	logcycle   = 0.05;
	deltacycle = 1.25;
	set_dutycycle( logcycle );     
	TheAddress = pattern;
	timecount = 10;   
	set_timer0( 100-1 );	// set timer to interrupt every 100*20/sysclock sec

	while(1)	{
		if( !timecount ){
			timecount = 10;		// change duty cycle every 10*100*20/sysclock
			logcycle = logcycle * deltacycle;
			if( logcycle > 1.0 ) deltacycle = 0.50;
			else if( logcycle < 0.05 ) deltacycle = 1.50;
			set_dutycycle( logcycle );
		}
		hitwd();	runwatch();
	}
}
// Set the ON time of the dutycle as a fraction of 1; e.g. 0.50 is 50%. This
// function distributes the ON and OFF times across the span of the cycle
// instead of just one section of ON and one section OFF. Portions are
// allocated based on the ratio of ON and OFF time until one of them runs out. 

void set_dutycycle( float dutycycle ){
	int i,j;
	int   ratio;
	float residue;
	float resadd;
	char markmore, markless;
	int more, less;
			
	if( dutycycle >= 1.0 ){
		for(i=0;i<SIZE;i++) pattern[i] = 1;	return;
	}
	if( dutycycle <= 0.0 ){
		for(i=0;i<SIZE;i++) pattern[i] = 0;	return;
	}
	if( dutycycle < 0.50 ){
		less = SIZE * dutycycle;
		more = SIZE - less;
		markmore = 0;
		markless = 1;
	}else{
		more = SIZE *dutycycle;
		less = SIZE - more;
		markmore = 1;
		markless = 0;  
	}
	residue = (float)more / (float)less;
	ratio   = more/less;
	resadd  = residue - ratio;

	i = 0; residue = 0.0;
	while( i < SIZE ){
		if(residue > 1.0){				// correct for fractional increments
			pattern[i++] = markmore;
			residue = residue - 1.0;
			more--;
			if( more == 0 ){
				while( i<SIZE ) pattern[i++] = markless; break;
			}
		}
		for( j=0;j<ratio;j++ ){
			pattern[i++] = markmore;
			more--;
			if( more == 0){
				while( i<SIZE ) pattern[i++] = markless; break;
			}
		}
		if( i < SIZE ){
			residue += resadd;
			pattern[i++] = markless;
			less--;
			if( less == 0 ){
				while(i<SIZE) pattern[i++] = markmore;
			}
		}
	}
}

// Set up timer0 to interrupt every load_value*20/sysclock() seconds. 
void set_timer0( unsigned int load_value ){
	DI();
	outport( TCR,    inport(TCR) & '\B11101110');	// inhibit TIMER0 interrupt
	outport( TMDR0L, load_value &        0xff  );
	outport( TMDR0H, (load_value >> 8) & 0xff  );
	outport( RLDR0L, load_value        & 0xff  );
	outport( RLDR0H, (load_value >> 8) & 0xff  );
	outport( TCR,    inport(TCR) | '\B00010001');
	EI();
}

// Timer0 interrupt service routine. One byte of the pattern array is output
// every time this timer interrupt happens. When a pattern is finished, it is
// restarted again.
#INT_VEC PRT0_VEC timer0
interrupt timer0(){
	char temp;
	inport(TCR);
	inport(TMDR0L);

	timecount--;				// decrement timecount
	temp = *TheAddress++;
	outport(CS1, temp);
	outport(CS1+1, temp);
	outport(CS1+2, temp);
	outport(CS1+3, temp);

	if(--datacount == 0)	{
		TheAddress = pattern;
		datacount = SIZE;
	}
}

	



