#use eziolp31.lib
#use vdriver.lib
#use aascz0.lib
#use aascz1.lib

CHANNEL hostChan, z1Chan;
unsigned int nextDelay;

#define FLUSHCHAR '|'
#define PACKETSIZE 16
#define TERMINATOR 13
#define BUFSIZE 128

#define DIOWR 0x4040
#define DIORD 0x4020
#define DOUT 0x4060
#define DIN  0x4040
#define port_nDOE 0x40a4
#define port_n232TEN 0x4082
#define port_232EN 0x4083
#define port_LED 0x40a0
#define port_nPWREN 0x40a5
#define port_nEN_DKS 0x40a0

int testCase;

/* beginning of slave communication utility functions */

int waitForBufFree(CHANNEL chan, unsigned int size, unsigned int timeout) {
	auto unsigned long int timeMark;

	timeMark = MS_TIMER;
	while (((MS_TIMER - timeMark < timeout) || !timeout) &&
			 aascReadBufFree(chan) < size) hitwd();
	return (aascReadBufFree(chan) >= size) ? 0 : -1;
}

int waitForPacket(CHANNEL chan, char term, unsigned int timeout) {
	auto unsigned long int timeMark;
	auto int result;

	timeMark = MS_TIMER;
	while (((MS_TIMER - timeMark < timeout) || !timeout) &&
			 !(result = aascScanTerm(chan,term))) hitwd();
	return result;
}

void waitForClear(CHANNEL ch);

int rOutport(int node, unsigned int port, char value) {
	auto int result;
	auto char localBuf[4];
	auto int tmp;
	
	result = waitForBufFree(z1Chan, PACKETSIZE, 1000);
	if (result) {
		return result;		//	operation failed!
	} else {
		aascPrintf(z1Chan,"%1dO%04x,%02x\r",node,port,value);
		result = waitForPacket(z1Chan,TERMINATOR,1000);
		if (result != 1) {
			return -1;
		} else {
			if (aascReadBlk(z1Chan, localBuf, result, 1) != result) return -1;
		}
		return 0;
	}
}

int rOutportSend(int node, unsigned port, char value) {
	auto int result;
	auto char localBuf[4];
	auto int tmp;
	
	result = waitForBufFree(z1Chan, PACKETSIZE, 1000);
	if (result) {
		return result;		//	operation failed!
	} else {
		aascPrintf(z1Chan,"%1dO%04x,%02x\r",node,port,value);
		waitForClear(z1Chan);
		return 0;
	}
}

int rInport(int node, unsigned int port) {
	auto int result;
	auto char localBuf[10];
	
	result = waitForBufFree(z1Chan, PACKETSIZE, 1000);
	if (result) {
		return result;		//	operation failed!
	} else {
		aascPrintf(z1Chan,"%1dI%04x\r",node,port);
		result = waitForPacket(z1Chan,TERMINATOR,1000);
		if (result < 0) return result;
		else {
			aascReadBlk(z1Chan, localBuf, result, 1);
			return (int)strtol(localBuf, NULL, 16);
		}
	}
}

int rAin(int node, unsigned int port) {
	auto int result;
	auto char localBuf[10];
	
	result = waitForBufFree(z1Chan, PACKETSIZE, 1000);
	if (result) {
		return result;		//	operation failed!
	} else {
		aascPrintf(z1Chan,"%1di%04x\r",node,port);
		result = waitForPacket(z1Chan,TERMINATOR,1000);
		if (result < 0) return result;
		else {
			aascReadBlk(z1Chan, localBuf, result, 1);
			return (int)strtol(localBuf, NULL, 16);
		}
	}
}

int rAinSend(int node, unsigned int port) {
	auto int result;
	
	result = waitForBufFree(z1Chan, PACKETSIZE, 1000);
	if (result) {
		return result;		//	operation failed!
	} else {
		aascPrintf(z1Chan,"%1di%04x\r",node,port);
		while (aascWriteBufLeft(z1Chan)) hitwd();
		return 0;
	}
}

int rAinRec() {
	auto int result;
	auto char localBuf[10];
	
	result = waitForPacket(z1Chan,TERMINATOR,1000);
	if (result < 0) return result;
	else {
		aascReadBlk(z1Chan, localBuf, result, 1);
		return (int)strtol(localBuf, NULL, 16);
	}
}

int rDelay(int node, unsigned int msDelay) {
	auto int result;
	
	result = waitForBufFree(z1Chan, PACKETSIZE, 1000);
	if (result) {
		return result;		//	operation failed!
	} else {
		aascPrintf(z1Chan,"%1dD%04x\r",node,msDelay);
		return 0;
	}
}

/* end of slave communication utility functions */

/* beginning of host communication utility functions */

#define FLOAT_LEN 16

float readFloat(CHANNEL ch, char *prompt) {
	auto float f;
	auto char localBuf[FLOAT_LEN];
	auto int offset;
	auto int tmp;

	do {
		tmp = strlen(prompt);
		while (aascWriteBufFree(ch) < tmp) hitwd();
		aascWriteBlk(ch, prompt, tmp, 1);
		offset = 0;
		while ((offset = aascGets(ch,localBuf,sizeof(localBuf),offset,1)) >= 0) hitwd();
		f = atof(localBuf);
	} while (_xtoxErr);
	return f;
}

int	qYesNo(CHANNEL ch, char *prompt) {
	auto int tmp;
	
	tmp = strlen(prompt);
	while (aascWriteBufFree(ch) < tmp) hitwd();
	aascWriteBlk(ch, prompt, tmp, 1);
	while (!(tmp = aascSelectKey(ch, "NnYy", 1))) hitwd();
	return (tmp+1)/2 - 1;		
}

void	waitAnyKey(CHANNEL ch) {
	auto char localBuf;
	
	while (!aascReadBufLeft(ch)) hitwd();
	aascReadChar(ch, &localBuf);
}

void busyDelay(unsigned long ms) {
	auto unsigned long timeMark;
	
	timeMark = MS_TIMER;
	while (MS_TIMER - timeMark < ms) hitwd();
}

void waitForClear(CHANNEL ch) {
	while (aascWriteBufLeft(ch)) hitwd();
}

/* end of host communication support utilities */

/* beginning of test procedures */

int	testDInOut() {
	auto unsigned int counter;
	auto int error;
	auto int tmpVal;
	auto unsigned long timebase;
	
	//	40a4 is /DOE
	outport(port_nDOE,1);		//	local for input
	rOutport(0,port_nDOE,0);	//	remote for output
	counter = 0;
	error = 0;
	while (!error && counter < 0x100) {
		error = rOutport(0,DIOWR,counter);
		if (inport(DIORD) != counter) {
			// problem reading back!
			error = -1;
		}
		hitwd();
		++counter;
	}
	rOutport(0,port_nDOE,1);
	outport(port_nDOE,0);
	counter = 0;
	while (!error && counter < 0x100) {
		outport(DIOWR,counter);
		tmpVal = rInport(0,DIORD);
		if (tmpVal < 0) error = -1;
		else if (rInport(0,DIORD) != counter) {
			error = -1;
		}
		hitwd();
		++counter;
	}
	outport(port_nDOE,1);
	outport(DIOWR,0x0);
	if (rInport(0,DIORD) != 0xff) error = -1;	//	should read pull up
	outport(port_nDOE,0);
	if (rInport(0,DIORD) != 0) error = -1;		//	should read output 0
	outport(port_nDOE,1);
	return error;
}

int	testDOut() {
	auto unsigned outVal;
	auto int error;
	auto int tmp;

	outVal = 0;
	error = 0;
	while (!error && (outVal < 0x100)) {
		outport(DOUT,outVal);
		tmp = rInport(0,DIN);
		if ((tmp < 0) || ((tmp & 0xf) != (outVal & 0xf))) {
			error = -1;
			break;
		}
		tmp = rInport(1,DIN);
		if ((tmp < 0) || ((tmp & 0xf) != (outVal >> 4))) {
			error = -1;
			break;
		}
		++outVal;
	}
	outport(DOUT,0);
	return error;
}

int	testDIn() {
	auto unsigned outVal;
	auto int error;
	auto int tmp;

	outVal = 0;
	error = 0;
	while (!error && (outVal < 0x10)) {
		tmp = rOutport(0,DOUT,outVal);
		if ((tmp < 0) || ((inport(DIN) & 0xf) != outVal)) {
			error = -1;
			break;
		}
		++outVal;
	}
	rOutport(0,DOUT,0);
	return error;
}

int	testAIn() {
	auto unsigned outVal;
	auto int error;
	auto int tmp;
	auto int i;
	auto char localBuf[80];
	auto float f1, f2;
	auto unsigned u1, u2;

	outVal = 0;
	error = 0;
	if (_eioBrdAI(0) < 0) error = -1;
	for (i = 0; !error && (i < 4); ++i) {
		outVal = 1 << (i + 4);
		tmp = rOutport(0,DOUT,0);
		if (tmp < 0) {
			error = -1;
			break;
		} else {
			sprintf(localBuf,"Recalibrate ADC Channel %d? [Yy/Nn] ",i);
			if (qYesNo(hostChan,localBuf)) {
				sprintf(localBuf,"Input Voltage at H2:%d: ",35+i);
				f1 = readFloat(hostChan,localBuf);
				u1 = _eioBrdAI(i);
				if (u1 > 4095) {
					error = -1;
					break;
				}
				tmp = rOutport(0,DOUT,outVal);
				if (tmp < 0) {
					error = -1;
					break;
				}
				f2 = readFloat(hostChan,localBuf);
				u2 = _eioBrdAI(i);
				eioBrdACalib(i,u1,u2,f1,f2);
			}
			rOutport(0,DOUT,0);
			aascPrintf(hostChan,"reading %5.2f V at H2:%d, press a key\n\r",eioBrdAI(i), 35+i);
			waitAnyKey(hostChan);
			rOutport(0,DOUT,outVal);
			aascPrintf(hostChan,"reading %5.2f V at H2:%d, press a key\n\r",eioBrdAI(i), 35+i);
			waitAnyKey(hostChan);
		}
	}
	rOutport(0,DOUT,0);
	return error;
}

int	testRTC() {
	auto struct tm t;
	auto int sec;
	auto unsigned long msTimeMark;
	auto unsigned long tmp;
	auto int i;

	tm_rd(&t);
	for (i = 0; i < 1; ++i) {
		sec = t.tm_sec;
		msTimeMark = MS_TIMER;
		do {
			tm_rd(&t);
			hitwd();
		} while (((tmp = MS_TIMER) - msTimeMark < 2000) && (sec == t.tm_sec));
	}
	msTimeMark = tmp;
	if (sec != t.tm_sec) {
		sec = t.tm_sec;
		do {
			tm_rd(&t);
			hitwd();
		} while (((tmp = MS_TIMER - msTimeMark) < 2000) && (sec == t.tm_sec));
		if ((tmp < 1005) && (tmp > 995)) return 0;
		else return -1;
	} else {
		return -1;
	}
}

int	testWatchdog() {
	static int flag;

	segchain sysSupRstChain {
		flag = 0;
	}
	switch (flag) {
		case 0:
			//	just been super reset, first time pass
			++flag;
			waitForClear(hostChan);
			DI();			//	force a WD reset
			for(;;);
			break;
		case 1:
			//	should be reset due to WD
			flag = 0;
			if (sysIsWDTO()) return 0;
			else return 1;
		default:
			flag = 0;
			return -1;
	}
}

int	testRTCAlm() {
	static int flag;
	static unsigned long secs;
	auto struct tm t1;
	unsigned int u;
	unsigned long int tmp;

	segchain sysSupRstChain {
		flag = 0;
	}

	switch (flag) {
		case 0:
			++flag;
			rtcRead(0xc);			//	read to clear alarm flag
			rtcSwWIE(0);			//	switch off wake up interrupt
			rtcSwWF(0);				//	clear the wake up flag, otherwise /PWR remains high
			eioBrdDO(24,1);		//	turn on LED
			rtcInit();				//	initialize RTC operational parameters
			tm_rd(&t1);				//	read the current time
			secs = mktime(&t1);	//	compute the current time
			secs += 3;				//	want to wake up in 2secs
			mktm(&t1,secs);		//	put wakeup time in t2
			rtcSetAlmTime(&t1);	//	set the alarm time
			rtcSwAIE(1);			//	set alarm enable
			rtcSwWIE(1);			//	enable wake up interrupt
			rtcSwABE(1);			//	enable aux. battery (Vbaux)
			swEnbPower(0);			//	turn off main power now (via the latch)
			rtcSwPAB(1);			//	need to switch the RTC pin as well
			for (;;) hitwd();		//	power should be turned off
		case 1:
			rtcInit();
			tm_rd(&t1);
			rtcSwAIE(0);			//	set alarm disable
			rtcSwWIE(0);			//	disable wake up interrupt
			tmp = mktime(&t1);
			if ((tmp < secs - 1) || (tmp > secs + 1)) return -1;
			else return 0;
		default:
			flag = 0;
			return -1;
	}	
}

int	test232TEN() {
	auto int tmp;
	
	aascPrintf(hostChan,"if you see this line only, press 1\n\r");
	waitForClear(hostChan);
	busyDelay(100);
	outport(port_n232TEN,0);
	aascPrintf(hostChan,"if you also see this line, press 2\n\r");
	waitForClear(hostChan);
	busyDelay(100);
	outport(port_n232TEN,1);
	while (!(tmp = aascSelectKey(hostChan,"12",1))) hitwd();
	if (tmp == 1) {
		return 0;
	} else {
		return -1;
	}
}

int	test232EN() {
	auto int tmp;
	
	aascPrintf(hostChan,"if you see this line only, press 1\n\r");
	waitForClear(hostChan);
	busyDelay(100);
	outport(port_232EN,0);
	aascPrintf(hostChan,"if you also see this line, press 2\n\r");
	waitForClear(hostChan);
	busyDelay(100);
	outport(port_232EN,1);
	while (!(tmp = aascSelectKey(hostChan,"12",1))) hitwd();
	if (tmp == 1) {
		aascPrintf(hostChan,"Press some keys before the next prompt\n\r");
		waitForClear(hostChan);
		busyDelay(100);
		outport(port_232EN,0);
		aascFlushRdBuf(hostChan);
		busyDelay(5000);
		if (aascReadBufLeft(hostChan)) tmp = -1;
		else tmp = 0;
		outport(port_232EN,1);
		if (tmp) {
			aascPrintf(hostChan,"reception was possible\n\r");
		} else {
			aascPrintf(hostChan,"reception was probably disabled as expected\n\r");
		}
		aascFlushRdBuf(hostChan);
		return tmp;
	} else {
		return -1;
	}
}

int	testFlash() {
	auto int i;
	auto struct _flashInfo fInfo;
	extern char OPMODE;
	auto char intMode;

	if (OPMODE & 0x80) {
		aascPrintf(hostChan,"%dkbyte flash in U8.\n\r",
			(OPMODE & 0x10) ? 128 : (OPMODE & 0x20) ? 256 : 0);
	} else {
		aascPrintf(hostChan,"no recognizable flash in U8.\n\r");
	}
	waitForClear(hostChan);
	intMode = sysDI();
	i = sysChk2ndFlash(&fInfo);
	sysRestoreI(intMode);
	if (i < 0) {
		aascPrintf(hostChan,"no recognizable flash in U25.\n\r");
	} else {
		aascPrintf(hostChan,"%dkbyte flash in U25.\n\r",fInfo.sectorSize);
	}
	return i;
}

int	testKS() {
	static int flag;
	static unsigned long secs;
	auto struct tm t1;
	unsigned int u;
	unsigned long int tmp;

	segchain sysSupRstChain {
		flag = 0;
	}

	switch (flag) {
		case 0:
			++flag;
			rOutport(0,DOUT,0xf);	//	disable kickstart pin first
			outport(port_nEN_DKS,1);	//	disable kick start from DIN0
			rtcSwKF(0);				//	clear the kick start flag, otherwise /PWR remains high
			tm_rd(&t1);					//	read the current time
			secs = mktime(&t1);		//	compute the current time
			secs += 3;					//	want to wake up in 10secs
			rDelay(0,3000);			//	delay action by 5000ms
			rOutportSend(0,DOUT,0);
			outport(port_nEN_DKS,0);	//	enable kick start from DIN0 (active low)
			rtcSwKSE(1);			//	switch on kick start enable
			swEnbPower(0);			//	turn off main power now (via the latch)
			rtcSwPAB(1);			//	need to switch the RTC pin as well
			for (;;) hitwd();		//	power should be turned off
		case 1:
			rtcInit();
			rtcSwKSE(0);			//	disable kickstart
			outport(port_nEN_DKS,1);	//	disable kick start from DIN0
			tm_rd(&t1);
			tmp = mktime(&t1);
			if ((tmp < secs - 1) || (tmp > secs + 1)) return -1;
			else return 0;
		default:
			flag = 0;
			return -1;
	}	
}

void	testDLS() {
}
	
/* end of test procedures */

void findResetCause() {
	if (chkSuperReset()) {		// detects a super reset
	} else {
//		if (_sysIsPwrFail()) {		// set flag to remember power fail
//		} else
		if (_sysIsWDTO()) {	// remember watch dog timer flag
		} else {
		}
	}
}

main() {
	static char inBuf[2][BUFSIZE],outBuf[2][BUFSIZE];
	unsigned long timeMark;
	struct tm t;
	int result;

	segchain sysSupRstChain {
		testCase = 0;
		mktm(&t,0);
		tm_wr(&t);
	}

	nextDelay = 0;
	
	rtcInit();
	
	VdInit();
	
	eioBrdInit(0);

	outport(DIOWR,0);
	outport(DOUT,0);
	
	//	initialize the serial channels
	findResetCause();
	if (sysIsSuperReset()) {
		defClrSuperReset();
	} else if (sysIsWDTO()) {
	} else {
	}
	
	hostChan = aascOpen(DEV_Z0,0,ASCI_PARAM_8N1 | (ASCI_PARAM_1200*16), NULL);
	z1Chan = aascOpen(DEV_Z1,0,ASCI_PARAM_8N1 | (ASCI_PARAM_1200*16), NULL);
	aascSetWriteBuf(hostChan,outBuf[0],sizeof(outBuf[0]));
	aascSetWriteBuf(z1Chan,outBuf[1],sizeof(outBuf[1]));
	aascSetReadBuf(hostChan,inBuf[0],sizeof(inBuf[0]));
	aascSetReadBuf(z1Chan,inBuf[1],sizeof(inBuf[1]));
	aascTxSwitch(hostChan,1);
	aascTxSwitch(z1Chan,1);
	aascRxSwitch(hostChan,1);
	aascRxSwitch(z1Chan,1);

	outport(0x4084,1);
	outport(0x4083,1);
	outport(0x4082,1);

	if (testCase == 0) {
		aascPrintf(hostChan,"Begin test now...\n\r");
	}
	
	switch (testCase) {
		case 0:
			timeMark = MS_TIMER;
			while (MS_TIMER - timeMark < 2000) hitwd();
			result = testDInOut();
			aascPrintf(hostChan,"Dinout test returns %d\n\r",result);
			result = testDOut();
			aascPrintf(hostChan,"Dout test returns %d\n\r",result);
			result = testDIn();
			aascPrintf(hostChan,"Din test returns %d\n\r",result);
			result = testAIn();
			aascPrintf(hostChan,"Ain test returns %d\n\r",result);
			result = testRTC();
			aascPrintf(hostChan,"RTC test returns %d\n\r", result);
			result = test232TEN();
			aascPrintf(hostChan,"232TEN test returns %d\n\r", result);
			result = test232EN();
			aascPrintf(hostChan,"232EN test returns %d\n\r", result);
			result = testFlash();
			aascPrintf(hostChan,"flash test returns %d\n\r", result);
			++testCase;
		case 1:
			result = testWatchdog();
			aascPrintf(hostChan,"Watchdog test returns %d\n\r", result);
			waitForClear(hostChan);
			++testCase;
		case 2:
			result = testRTCAlm();
			aascPrintf(hostChan,"RTC Alarm test returns %d\n\r", result);
			++testCase;
		case 3:
			result = testKS();
			aascPrintf(hostChan,"KS test returns %d\n\r", result);
			++testCase;
		case 4:
			testDLS();
			++testCase;
		default:
			testCase = 0;
	}
	
	if (qYesNo(hostChan,"Do you wish to retest this unit? [Yy/Nn]")) {
		sysForceSupRst();
	} else {
		aascPrintf(hostChan, "Power down and remove this unit.\n\r");
		while (aascWriteBufLeft(hostChan)) hitwd();
		for (;;) hitwd();
	}
}