/*
	DIAG.C

	This is a diagnostic program designed to check the features of a PK23xx
	controller.

	A STDIO-based menuing system is used for selecting the subsystem to test.
	Subsystems include digital i/o, analog i/o, rs232 and rs485	communications.

	The analog i/o consist of a single resistive measurement input (RMI) channel
	and seven pulse width modulated (PWM) output channels.

	See the PK2300 manual for appropriate jumper settings for each function.
*/

#define RUNKERNEL 1
#use srtk.lib
#use vdriver.lib
#use eziopk23.lib
#use aascdio.lib
#use aascz0.lib
#use aascz1.lib

struct _Channel *stdChan;
struct _Channel *z1;

void print16Bits(unsigned word) {
	aascPrintf(stdChan,"%s%s%s%s"	,
				__nibblemap[word >> 12],
				__nibblemap[(word >> 8) & 0xf],
				__nibblemap[(word >> 4) & 0xf],
				__nibblemap[word & 0xf]);
}

void scanDI(char oneShot) {
	auto char inByte;

	aascPrintf(stdChan,"\n\rScanning Input Channels [hit a key to stop]\n\r");
	aascPrintf(stdChan,"c h a n n e l s\n\r");
	aascPrintf(stdChan,"1111110000000000\n\r");
	aascPrintf(stdChan,"5432109876543210\n\r");
	aascPrintf(stdChan,"XXXX------------ (X not connected)\n\r");
	if (!oneShot) {
		while (!aascReadBufLeft(stdChan)) {
			print16Bits((inport(0x4161)<<8)|inport(0x4160));
			while(!aascWriteChar(stdChan,'\r'));
		}
		aascReadChar(stdChan,&inByte);
	} else {
		print16Bits((inport(0x4161)<<8)|inport(0x4160));
	}
	while (!aascWriteChar(stdChan,'\n'));
	while (!aascWriteChar(stdChan,'\r'));
}

void scanAI(char oneShot) {

	auto char inByte;

	aascPrintf(stdChan,"\n\rScanning Analog Input [hit a key to stop]\n\r");
	if (!oneShot) {
		while (!aascReadBufLeft(stdChan)) {
			aascPrintf(stdChan,"%5u\r",(unsigned)eioBrdAI(0));
		}
		aascReadChar(stdChan,&inByte);
	}
	 else {
		aascPrintf(stdChan,"%5u\r",(unsigned)eioBrdAI(0));
	}
	while (!aascWriteChar(stdChan,'\n'));
	while (!aascWriteChar(stdChan,'\r'));
}

void switchDO(char onOff) {
	auto channel;
	
	aascPrintf(stdChan,
		"select output channel DigOut[0123456789ab] LED[cd]\n\r");
	while (!(channel = aascSelectKey(stdChan,"0123456789abcd",1)));
	if (channel <= 12) eioBrdDO(channel-1,onOff);
	else {
		outport(0x4140-12+channel,onOff);
	}
}

void specAO() {
	auto channel;
	auto int value;
	auto char localStr[16];
	auto int offset;
	
	aascPrintf(stdChan,
		"select output channel DigOut[0123]\n\r");
	while (!(channel = aascSelectKey(stdChan,"0123",1)));
	aascPrintf(stdChan,
		"specify the PWM off duty (#divisions out of 256)\n\r");
	value = -1;
	offset = 0;
	do {
		while ((offset =
			aascGets(stdChan,
			localStr,sizeof(localStr),offset,1)) != -1);
		value = atoi(localStr);
	} while (_xtoxErr || (value < 0) || (value > 256));
	aascPrintf(stdChan,"analog output %d\n\r",value);
	eioBrdAO(channel-1,value);
}

int digIOMenu() {
	auto char selection;
	
	aascPrintf(stdChan,"\n\rDigital I/O Menu\n\r");
	aascPrintf(stdChan,"-----------------\n\r");
	aascPrintf(stdChan,"0 to Top Menu\n\r");
	aascPrintf(stdChan,"1 to scan input (continuous)\n\r");
	aascPrintf(stdChan,"2 to scan input (one shot)\n\r");
	aascPrintf(stdChan,"3 to disable output\n\r");
	aascPrintf(stdChan,"4 to enable output\n\r");
	while (!(selection = aascSelectKey(stdChan,"01234",1)));
	switch (selection-1) {
		case 0: return 0;
		case 1: scanDI(0); return 1;
		case 2: scanDI(1); return 1;
		case 3:
		case 4: switchDO(selection-4);
			return 1;
		default:
			return 0;
	}
}

int anIOMenu() {
	auto char selection;
	
	aascPrintf(stdChan,"\n\rAnalog I/O Menu\n\r");
	aascPrintf(stdChan,"---------------\n\r");
	aascPrintf(stdChan,"0 to Top Menu\n\r");
	aascPrintf(stdChan,"1 to scan input (continuous)\n\r");
	aascPrintf(stdChan,"2 to scan input (one shot)\n\r");
	aascPrintf(stdChan,"3 to output\n\r");
	while (!(selection = aascSelectKey(stdChan,"0123",1)));
	switch (selection-1) {
		case 0: return 0;
		case 1: scanAI(0); return 1;
		case 2: scanAI(1); return 1;
		case 3: specAO(); return 1;
		default:
			return 0;
	}
}

int sw485(int OnOff) {
	outport(0x4140,OnOff?1:0);
}

void z1Echo(int is485) {
	auto char tmpChar;
	auto int cts1;
	auto unsigned cnt;
	auto char r1Buf[256], w1Buf[256];
	static char *stdString = "0123456";
	auto char localStr1[8];
	auto int stdStringLen;
	auto unsigned readCnt;
	static CoData x,y;
	auto unsigned long ms;

	aascPrintf(stdChan,"This test requires port z1 configured\n\r");
	if (is485) {
		aascPrintf(stdChan,"for RS-485. You can use a scope to see\n\r");
		aascPrintf(stdChan,"the actual output.\n\r");
	} else {
		aascPrintf(stdChan,"for RS-232 loop back. You can use a scope to see\n\r");
		aascPrintf(stdChan,"the actual output.\n\r");
	}
	aascPrintf(stdChan,"Press any key to stop\n\r");
	z1 = aascOpen(DEV_Z1,0,ASCI_PARAM_1200*4|ASCI_PARAM_8N1,NULL);
	if (!z1) return;
	if (is485) aascSetCustTxSw(z1,sw485);
	aascSetReadBuf(z1,r1Buf,sizeof(r1Buf));
	aascSetWriteBuf(z1,w1Buf,sizeof(w1Buf));
	aascTxSwitch(z1,1);
	aascRxSwitch(z1,1);
	cts1 = 1;
	cnt = 0;
	stdStringLen = strlen(stdString);
	CoBegin(&x);
	CoBegin(&y);
	ms = MS_TIMER;
	while (!aascReadBufLeft(stdChan)) {
		costate x always_on {
			waitfor(cts1);
			cts1 = 0;
			waitfor(aascWriteBlk(z1,stdString,stdStringLen,1));
			ms = MS_TIMER;
		}
		if (!aascWriteBufLeft(z1)) {
			if (aascReadBlk(z1,localStr1,stdStringLen,1)) {
				if (is485) z1->customTxSwitch(0);
				cts1 = 1;
				localStr1[stdStringLen] = 0;
				if (strcmp(localStr1,stdString)) {
					aascPrintf(stdChan,"error in echoed string\n\r");
					goto errorExit;
				} else {
					aascPrintf(stdChan,"round %5u\r",++cnt);
				}
			} else if (MS_TIMER - ms > 300) {
				aascPrintf(stdChan,"characters not echoed\n\r");
				goto errorExit;
			}
		}
	}
	aascReadChar(stdChan,&tmpChar);
 errorExit:
	if (is485) z1->customTxSwitch(0);
	aascClose(z1);
}

void z1232() {
}

int comIOMenu() {
	auto char selection;
	
	aascPrintf(stdChan,"\n\rCommunication I/O Menu\n\r");
	aascPrintf(stdChan,"--------------------------\n\r");
	aascPrintf(stdChan,"0 to Top Menu\n\r");
	aascPrintf(stdChan,"1 for RS-485 (local echo)test\n\r");
	aascPrintf(stdChan,"2 for RS-232 (loop back) test\n\r");
	while (!(selection = aascSelectKey(stdChan,"012",1)));
	switch (selection-1) {
		case 0: return 0;
		case 1: z1Echo(1); return 1;
		case 2: z1Echo(0); return 1;
		default:
			return 0;
	}
}

int topMenu() {
	auto char selection;

	aascPrintf(stdChan,"\n\rTop Menu\n\r");
	aascPrintf(stdChan,"--------\n\r");
	aascPrintf(stdChan,"0 to quit\n\r");
	aascPrintf(stdChan,"1 for digital submenu\n\r");
	aascPrintf(stdChan,"2 for analog submenu\n\r");
	aascPrintf(stdChan,"3 for communication submenu\n\r");
	while (!(selection = aascSelectKey(stdChan,"0123",1)));
	switch(selection-1) {
		case 0: return 0;
		case 1: while (digIOMenu()); return 1;
		case 2: while (anIOMenu()); return 1;
		case 3: while (comIOMenu()); return 1;
		default: return 1;
	}
}

char runWithDC;

#funcchain _srtk_lowtask _dio

void _dio() {
	if (runWithDC) {
		_dioServer();
	}
}

#funcchain _srtk_hightask _eioBrdAORf

main() {
	static char rBuf[256];
	static char wBuf[256];

	segchain _srtk_lowtask {
		switch (inport(0x4160) & 0xf) {
			case 0x5:	sysForceReset();
			case 0xa:	sysForceSupRst();
			default:
		}
	}
	
	if (_sysIsSuperReset()) {
	} else {
		if (_sysIsPwrFail()) {
		} else if (_sysIsWDTO()) {
		} else {
		}
	}
	
	VdInit();
	
	if (runWithDC = sysIsRunWithDC()) {
		stdChan = aascOpen(DEV_STDIO,0,0,NULL);
	} else {
		stdChan = aascOpen(DEV_Z0,0,ASCI_PARAM_1200*16|ASCI_PARAM_8N1,NULL);
	}
	if (!stdChan) exit(0);
	aascSetReadBuf(stdChan,rBuf,sizeof(rBuf));
	aascSetWriteBuf(stdChan,wBuf,sizeof(wBuf));
	aascTxSwitch(stdChan,1);
	aascRxSwitch(stdChan,1);
	_eioSetupAO1st();
	init_srtkernel();
	_eioSetupAI1st();
	aascPrintf(stdChan,"\r\n\nThis program is restarted due to a ");
	if (sysIsSuperReset()) {
		aascPrintf(stdChan,"super reset\n\r");
	}
	else if (sysIsPwrFail()) {
		aascPrintf(stdChan,"power fail\n\r");
	}
	else if (sysIsWDTO()) {
		aascPrintf(stdChan,"watchdog reset\n\r");
	}
	else {
		aascPrintf(stdChan,"regular reset\n\r");
	}
	while (topMenu());
	aascClose(stdChan);
}