/*
	SC1DMAPW.C

	CM71xx/Z1 and CM72xx/Z2

	This sample program sends a pulse width of prescribed duty cycle to LED
	D2 (CS1) and LED D5 (CS1+3). The pattern is sent out via DMA0 for D2 and DMA1
	for D5. Timer0 is used to generate the negative edges on /CS4 and /CS5 to
	drive DREQ1 and DREQ0, respectively. The main loop changes the duty cycle
	every 1 ms by 10%.
*/

// You must connect /CS4 to /DREQ1. Connect /CS5 to /DREQ0.

#use vdriver.lib

void set_timer0( unsigned int loadvalue );
void set_dutycycle( float dutycycle );

#define SIZE 1024        
char pattern[SIZE];				// to store the data pattern for DMA0 and DMA1
float logcycle;
float deltacycle;
unsigned long address;

main(){
	VdInit();						// initialize the virtual driver               

	logcycle = 0.05;
	deltacycle = 1.1;
	set_dutycycle(0.0);     
	address = phy_adr(pattern);
	DMA0_MIO( CS1  ,address,SIZE,1,0);	// set DMA0 for mem-to-IO operation
	DMA1_MIO( CS1+3,address,SIZE,1,0);	// set DMA1 for mem-to-IO operation
	set_timer0(100);		// set timer to interrupt every 100*20/sysclock() sec

	while(1){
		costate{
			waitfor(DelayMs(1L));				// change duty cycle every 1 ms
			logcycle = logcycle * deltacycle;
			if( logcycle > 1.0 )       deltacycle = 0.80;
			else if( logcycle < 0.05 ) deltacycle = 1.20;
			set_dutycycle(logcycle);
		}
		hitwd();	runwatch();
	}
}

// Set the ON time of the duty cycle 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. Drives DREQ1 and DREQ0 lines
// with CS4 and CS5, respectively.
#INT_VEC PRT0_VEC timer0
#asm
timer0::
	push af
	push bc
	in0  a,(TCR)			; service the interrupt
	in0  a,(TMDR0L)		; service the interrupt
	ld   bc,CS4
	out  (c),a				; generate pulse on CS4 for DREQ1
	ld   bc,CS5
	out  (c),a				; generate pulse on CS5 for DREQ0
	pop  bc
	pop  af
	ei
	ret
#endasm

// Interrupt service routine for DMA0.
#INT_VEC DMA0_VEC dma0_int;
interrupt void dma0_int(){
	outport( DSTAT,'\B00100000'|(inport(DSTAT)&'\B10101000') );
	DMA0_MIO( CS1,address,SIZE,1,0);		// retrigger the DMA operation
}

// Interrupt service routine for DMA1.    
#INT_VEC DMA1_VEC dma1_int;
interrupt void dma1_int(){
	outport( DSTAT,'\B00010000'|(inport(DSTAT)&'\B01010100') );
	DMA1_MIO( CS1+3,address,SIZE,1,0);	// retrigger the DMA operation      
}
/**/