/*
	DMAPWM2.C

	This program demonstrates how to use the DMA-driven PWM.
*/

#define RUNKERNEL 1
#define _PWMDMAPort _HV_BANK
#use vdriver.lib
#use srtk.lib
#use eziopk23.lib

#define PWMLEN	16
#define PWMOVR	16

#define PWM_1ON		0x80
#define PWM_1OFF		0x00
#define PWM_2ON		0x81
#define PWM_2OFF		0x01
#define PWM_3ON		0x82
#define PWM_3OFF		0x02
#define PWM_NOOP		0x07

#define PWMWVLEN 64

char PWMBuffer[2][(PWMLEN+PWMOVR+1)*256];

struct _pwmTask {
	char onChar, offChar;
	char offset;
	char dim;
	int sign;
	CoData cd;
	unsigned long peakTime;
} pwmTask[3];

#funcchain _srtk_hightask _eioBrdAORf

void pwmStep(char *pBufStart, struct _pwmTask *pT) {
	auto CoData *pCD;

	pCD = &pT->cd;
	costate pCD always_on {
		if (pT->dim != PWMWVLEN)
			dmapwmSetBuf(pBufStart+pT->offset+(pT->dim%PWMWVLEN)*3,
				PWMLEN+PWMOVR,
				PWMWVLEN*3,
				(!pT->dim)?pT->offChar:PWM_NOOP);
		pT->dim += pT->sign;
		if (pT->dim == 0) {
			pT->sign = 1;
			pT->peakTime = MS_TIMER;
		}
		else if (pT->dim == PWMWVLEN) pT->sign = -1;
		dmapwmSetBuf(pBufStart+pT->offset+(pT->dim%PWMWVLEN)*3,
			PWMLEN+PWMOVR,
			PWMWVLEN*3,
			(pT->dim != PWMWVLEN)?pT->onChar:pT->offChar);
		waitfor(IntervalMs((PWMWVLEN-pT->dim)/4));
	}
}

void initPWM(
	unsigned i,
	char onChar,
	char offChar,
	char offset,
	char dim,
	int sign,
	char *pBufStart,
	char step) {
	pwmTask[i].onChar = onChar;
	pwmTask[i].offChar = offChar;
	pwmTask[i].offset = offset;
	pwmTask[i].dim = dim;
	pwmTask[i].sign = sign;
	CoBegin(&pwmTask[i].cd);
	dmapwmSetBuf(pBufStart+offset,PWMLEN+PWMOVR,PWMWVLEN*3,offChar);
	if (dim != PWMWVLEN)
		dmapwmSetBuf(pBufStart+offset+3*dim,PWMLEN+PWMOVR,PWMWVLEN*3,onChar);
}

main() {
	auto char dim;
	auto char *pBufStart;
	auto int sign;
	auto int activeBuf;
	auto int i;

	pBufStart = dmapwmBufBeg(PWMBuffer[0]);

	memset(PWMBuffer[0],PWM_NOOP,sizeof(PWMBuffer[0]));
	activeBuf = 0;
	for (i = 0; i < 2; ++i) {
		initPWM(0,PWM_1ON,PWM_1OFF,0,64,-1,dmapwmBufBeg(PWMBuffer[i]),3);	
		initPWM(1,PWM_2ON,PWM_2OFF,1,20,1,dmapwmBufBeg(PWMBuffer[i]),3);	
		initPWM(2,PWM_3ON,PWM_3OFF,2,0,1,dmapwmBufBeg(PWMBuffer[i]),3);
	}
	VdInit();
	init_srtkernel();
	dmapwmInit((unsigned)((xmadr(PWMBuffer[0])+255)>>8),
		PWMLEN,PWMOVR,_HV_BANK,4);
	while (inport(BCR0H)) {
		pwmStep(dmapwmBufBeg(PWMBuffer[!activeBuf]),&pwmTask[0]);
		pwmStep(dmapwmBufBeg(PWMBuffer[!activeBuf]),&pwmTask[1]);
		pwmStep(dmapwmBufBeg(PWMBuffer[!activeBuf]),&pwmTask[2]);
		dmapwmSwBuf((unsigned)((xmadr(PWMBuffer[!activeBuf])+255) >> 8));
		memcpy(PWMBuffer[activeBuf],PWMBuffer[!activeBuf],sizeof(PWMBuffer[0]));
		activeBuf = !activeBuf;
		runwatch();
	}
}