///////////////////////////////////////////////////
///////////////////////////////////////////////////
// File: H2Olib_Jason.c
// Description: Provides all functions to control
//				H2O.
///////////////////////////////////////////////////
///////////////////////////////////////////////////

#include "H2O_Jason.h"

///////////////////////////////////////////////////
///////////////////////////////////////////////////
// QPB Definitions (for SPI Bus)
///////////////////////////////////////////////////
///////////////////////////////////////////////////
static QPB		*QPB_MAX146_Slot = NULL;
static QPB		*QPB_SPI4MOT_Slot = NULL;

QPBDev MAX146DevTemplate = 
	{
	MAX146_SLOT,		// qslot			our qspi slot
	"MAX146",			// devName			C string with device name (15 max)
	2000000,			// maxBaud			maximum baud rate in Hz for device
	16,					// bits				Bits Per Transfer
	iaLowSCK,			// clockPolar		SPI Clock Polarity
	captLead,			// clockPhase		SPI Clock Phase
	true,				// contCSMulti		Continue CS assert between mult xfrs
	true,				// autoTiming		Auto adjust timing to clock flag
	0,					// psDelaySCK		Min. Delay Before SCK (picoSecs)
	0,					// psDelayTXFR		Min. Delay After Transfer
	0,					// *rcvData			pointer to received data buffer
	0					// xfrCount			words transferred
	};

QPBDev SPI4MOTDevTemplate = 
	{
	SPI4MOT_SLOT,		// qslot			our qspi slot
	"SPI4MOT",			// devName			C string with device name (15 max)
	2000000,			// maxBaud			maximum baud rate in Hz for device
	8,					// bits				Bits Per Transfer
	iaLowSCK,			// clockPolar		SPI Clock Polarity
	captLead,			// clockPhase		SPI Clock Phase
	false,				// contCSMulti		Continue CS assert between mult xfrs
	true,				// autoTiming		Auto adjust timing to clock flag
	0,					// psDelaySCK		Min. Delay Before SCK (picoSecs)
	0,					// psDelayTXFR		Min. Delay After Transfer
	0,					// *rcvData			pointer to received data buffer
	0					// xfrCount			words transferred
	};



///////////////////////////////////////////////////
///////////////////////////////////////////////////
// Max146 Definitions
///////////////////////////////////////////////////
///////////////////////////////////////////////////
enum { 	Max146CMD		= 0x80	};
enum { 	Max146SGL		= 0x04, 		Max146DIF		= 0x00	};
enum { 	Max146UNI		= 0x08, 		Max146BIP		= 0x00	};
enum { 	Max146PDFull	= 0x00,			Max146PDFast	= 0x01,
		Max146INT		= 0x02, 		Max146EXT		= 0x03	};

const uchar Max146SglChSel[8] = 	// convert chan to single-ended selector
		{ 0x00, 0x40, 0x10, 0x50, 0x20, 0x60, 0x30, 0x70 };
const uchar Max146DifChSel[8] = 	// convert chan to differential selector
		{ 0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70 };


		
///////////////////////////////////////////////////
///////////////////////////////////////////////////
// Global Variables (correspond to H2O_Jason.h)
///////////////////////////////////////////////////
///////////////////////////////////////////////////
// Comports
TUPort 	*com1;
// Grab Motor
int		GrabTurnCount;
// For SPI Bus (only accessible in this file)
ushort 	g_SPI4MOT_byte = ALL_SPI4_OFF;



///////////////////////////////////////////////////
///////////////////////////////////////////////////
// OVERALL TPU FUNCTIONS
///////////////////////////////////////////////////
///////////////////////////////////////////////////

void InitializeTPU()
{	
	TPUInit();
	TPURun(true);
	TUInit(calloc, free);
	InitSPI();	//	 For SPI Bus and general pin setup
}

/****************************************************/

void ShutdownTPU()
{
	CloseComportUARTS();
	mFloodLightOff();
	
	TURelease();
	TPURun(false);
}



///////////////////////////////////////////////////
///////////////////////////////////////////////////
// SPI BUS Initialization
///////////////////////////////////////////////////
///////////////////////////////////////////////////

void InitSPI()
{
	QPBInit(true);
	
	QPB_SPI4MOT_Slot = QPBInitSlot(&SPI4MOTDevTemplate);		// Init SPI4 slot
	QPBTransact(QPBInitSlot(&SPI4MOTDevTemplate), 0, 1, &g_SPI4MOT_byte);	// All off

	QPB_MAX146_Slot = QPBInitSlot(&MAX146DevTemplate);			// Init MAX146 Slot
	
	PIORead(TPU1);		// Initialize Grab-limit as input
	PIORead(TPU2);		// Initialize Grab-turns as input
	PIORead(TPU3);		// Initialize Grab-footpad as input
	PIOClear(TPU4);		// Initialize Grab Sensor Power as output and turn off
}



///////////////////////////////////////////////////
///////////////////////////////////////////////////
// Comport Functions
///////////////////////////////////////////////////
///////////////////////////////////////////////////

void InitializeComportPins()
{
	DelaySecs(1, false);
	com1 = TUOpen(12, 11, 57600, TUGetDefaultParams());
	
	if(com1 == NULL)
	{
		fprintf(stderr, "Error opening Com1 as UART\n");
	}
	
	TURxFlush(com1);
	TUTxFlush(com1);
	
	TPUChannelFunctionSelect(9, DIO);	// Com1/2 Select
	TPUChannelPrioritySelect(9, tpuHighPriority);
	TPUChannelFunctionSelect(14, DIO);	// Com3/4 Select
	TPUChannelPrioritySelect(14, tpuHighPriority);
	
	PinClear(TPU5);
	TPUHostServiceCheckComplete(TPU5, true);
	PinClear(TPU14);
	TPUHostServiceCheckComplete(TPU14, true);
	PinSet(TPU9);
	TPUHostServiceCheckComplete(TPU9, true);
	
	SCIRxSetBuffered(true);
	SCITxSetBuffered(true);
}

/****************************************************/

void CloseComportUARTS()
{
	DelaySecs(1, false);
	PinClear(TPU5);
	TPUHostServiceCheckComplete(TPU5, true);
	PinClear(TPU9);
	TPUHostServiceCheckComplete(TPU9, true);
	PinClear(TPU14);
	TPUHostServiceCheckComplete(TPU14, true);
	
	if(com1 != NULL)
	{
		TURxFlush(com1);
		TUTxFlush(com1);
		TUClose(com1);
		com1 = NULL;
	}
}



///////////////////////////////////////////////////
///////////////////////////////////////////////////
// PC104 Commands
///////////////////////////////////////////////////
///////////////////////////////////////////////////

void Terminal()
{
	char inkey;
	
	
	if(com1 == NULL)
		InitializeComportPins();

	DisplayCommands();
	
	while(1)
	{
		if(tgetq(com1))
			cputc(tgetc(com1));
		
		if(cgetq())
		{
			inkey = cgetc();
			if(inkey == '@')
				break;
			else if(inkey == '#')
				SyncDateTime();
			else if(inkey == '$')
				TakePicture();
			else if(inkey == '%')
				SSHFTP(ON);
			else if(inkey == '^')
				SSHFTP(OFF);
			else if(inkey == '&')
				Shutdown();
			else if(inkey == '?')
				DisplayCommands();
			else
				tputc(com1, inkey);
		}
	}
	
	CloseComportUARTS();
}

/****************************************************/

void DisplayCommands()
{
	printf("\n\n================================\n");
	printf("Quick Command Keys:\n");
	printf("================================\n");
	printf(" @ = Exit Terminal\n");
	printf(" # = Sync Date/Time with CF2\n");
	printf(" $ = Take Picture\n");
	printf(" % = Turn on SSH/FTP Support\n");
	printf("     (will take ~45 seconds)\n");
	printf(" ^ = Turn off SSH/FTP Support\n");
	printf(" & = Shutdown PC104\n");
	printf(" ? = Redisplay this list\n");
	printf("================================\n\n");
}

/****************************************************/

void SyncDateTime()
{	
	char dateTimeStr[DATETIME_SIZ];
	
	DelaySecs(1, false);
	GetDateAndTime(dateTimeStr);
	TUTxPrintf(com1, "date %s\n", dateTimeStr);
	TUTxWaitCompletion(com1);
}

/****************************************************/

void TakePicture()
{	
	DelaySecs(1, false);
	switch(defaults.camnum)
	{
		case 1:
			TUTxPrintf(com1, "/root/takepicC1\n");
			break;
		case 2:
			TUTxPrintf(com1, "/root/takepicC2\n");
			break;
		case 3:
			TUTxPrintf(com1, "/root/takepicC3\n");
			break;
	}
	TUTxWaitCompletion(com1);
}

/****************************************************/

void SSHFTP(short ON_OFF)
{
	time_t start = RTCGetTime(NULL, NULL);
	int state = 0;
	char c;
	
	if(ON_OFF == ON)
	{
		printf("Turning on SSH and FTP Systems...\n");
		TUTxPrintf(com1, "telinit 2\n");
		TUTxWaitCompletion(com1);
		
		while(RTCGetTime(NULL, NULL) < start + 40)
		{
			if(tgetq(com1))
			{
				c = tgetc(com1);
				
				if(state == 0 && c == 'l')
					state = 1;
				else if(state == 1 && c == 'o')
					state = 2;
				else if(state == 2 && c == 'g')
					state = 3;
				else if(state == 3 && c == 'i')
					state = 4;
				else if(state == 4 && c == 'n')
					state = 5;
				else if(state == 5 && c == ':')
				{
					state = 0;
					TUTxPrintf(com1, "root\n");
					TUTxWaitCompletion(com1);
					
					start = RTCGetTime(NULL, NULL);
					
					while(RTCGetTime(NULL, NULL) < start + 20)
					{
						if(tgetq(com1))
						{
							c = tgetc(com1);
							
							if(state == 0 && c == 'P')
								state = 1;
							if(state == 1 && c == 'a')
								state = 2;
							if(state == 2 && c == 's')
								state = 3;
							if(state == 3 && c == 's')
							{
								TUTxPrintf(com1, "scripps\n");
								TUTxWaitCompletion(com1);
								return;
							}
						}
					}
				}
				else
					state = 0;
			}
		}
	}
	else
	{
		TUTxPrintf(com1, "telinit 1\n");
		TUTxWaitCompletion(com1);
	}
}

/****************************************************/

void Shutdown()
{
	DelaySecs(1, false);
	TUTxPrintf(com1, "shutdown -h now\n");
	TUTxWaitCompletion(com1);
}



///////////////////////////////////////////////////
///////////////////////////////////////////////////
// Grab Motor Routines
///////////////////////////////////////////////////
///////////////////////////////////////////////////

int GrabDownCycle(void)
{
/*	time_t	time_out;
  
	mSensorPwrOn();
	DelaySecs(1, false);

	printf("Grab down cycle start\n");
	time_out = time(NULL) + defaults.grab_timeout;	// Start time for timeout
	GrabTurnInterrupts(ON);					// Enable grab turn interrupt
	GrabTurnCount = 0;
	GrabMotor(DOWN);						// Move grab down
	printf("Going down!\n");
  
	while(1)
  	{
		if(GrabTurnCount >= defaults.grab_turns) break;		// Too many turns?
		if(mGrabFootpad()) break;			// Footpad activated?      
  		if(time(NULL) > time_out)			// Too much time?
  		{
			GrabMotor(STOP);
			mSensorPwrOff();
			GrabTurnInterrupts(OFF);
			printf("Grab down cycle time out\n");
			return 1;	
    	}
  	}         								// End of rack down main loop

  	GrabMotor(STOP);						// Benthic chambers planted--hopefully
  	mSensorPwrOff();
  	GrabTurnInterrupts(OFF);				// Disable grab turn interrupt
  	printf("Grab down\n");
  	printf("Grab down cycle end\n"); */
	return 0;
}

/************************************************************/

int GrabUpCycle(void)
{/*
  	time_t	time_out;
  	time_t	turn_time;
  	int		old_count = -1;

  	mSensorPwrOn();
	DelaySecs(1, false);

	printf("Grab up cycle start\n");
	time_out = time(NULL) + defaults.grab_timeout;	// Start time for timeout
	turn_time = time(NULL) + 30;
	GrabTurnInterrupts(ON);					// Enable grab turn interrupt
	GrabTurnCount = 0;
	GrabMotor(UP);         					// Move grab up
	printf("Going up!\n");
  
  	while(1)
  	{
  		if(GrabTurnCount >= defaults.grab_turns) break;		// Too many turns?     
  		if(mGrabLimit()) break;				// We're at the top!
	  	if(time(NULL) > time_out)			// Too much time?
  		{
			GrabMotor(STOP);
			mSensorPwrOff();
			GrabTurnInterrupts(OFF);
			printf("Grab up cycle time out\n");
			return 1;	
    	}
    	if(old_count != GrabTurnCount)
    	{
    		old_count = GrabTurnCount;
    		turn_time = time(NULL) + 30;
    	}
    	if(time(NULL) > turn_time)			// We're stuck?
    	{
    		printf("Grab motor may be stuck. Will try again in one minute\n");
    		GrabMotor(STOP);
    		DelaySecs(60, false);
    		printf("Starting grab up again\n");
    		turn_time = time(NULL) + 30;
    		GrabMotor(UP);
    		while(1)
    		{
    			if(GrabTurnCount >= defaults.grab_turns) break;
    			if(mGrabLimit()) break;
    			if(old_count != GrabTurnCount)
    			{
    				old_count = GrabTurnCount;
    				turn_time = time(NULL) + 30;
    			}
    			if(time(NULL) > turn_time)
    			{
    				GrabMotor(STOP);
    				mSensorPwrOff();
    				GrabTurnInterrupts(OFF);
    				printf("Grab motor stuck again. Will wait until ascent\n");
    				return 2;
    			}
    		}
    		GrabMotor(STOP);
    		mSensorPwrOff();
    		GrabTurnInterrupts(OFF);
    		printf("Grab at top\n");
    		printf("Grab up cycle end\n");
    		return 0;
    	}
  	}										// End of rack up main loop 

	GrabMotor(STOP);    
	mSensorPwrOff();
	GrabTurnInterrupts(OFF);
	printf("Grab at top\n");
  	printf("Grab up cycle end\n"); */
	return 0;
}

/***************************************************/

void GrabTurnInterrupts(short ON_OFF)
{/*
	int TPUChan = 2;
	
	if(ON_OFF == ON)
	{
		PIORead(TPUPinFromChan(TPUChan));
		
		IEVInsertCFunct(&GrabInterruptFunc, tpuVector + TPUChan);
		TPUClearInterrupt(TPUChan);
		TPUEnableInterrupt(TPUChan);
	}
	else
	{
		TPUDisableInterrupt(TPUChan);
	}*/
}

/***************************************************/

IEV_C_FUNCT(GrabInterruptFunc)
{/*
	short TPUChan = ((ievstack->fmtvct & 0x0FFF) >> 2) - tpuVector;
	
	if(!PIORead(TPUPinFromChan(TPUChan)))
	{
		GrabTurnCount++;
		printf("Turn detected [%d]!\n", GrabTurnCount);	
	}
	
	TPUClearInterrupt(TPUChan);*/
}



///////////////////////////////////////////////////
///////////////////////////////////////////////////
// SPI Routines
///////////////////////////////////////////////////
///////////////////////////////////////////////////

short Max146Sample(ushort chan, bool uni, bool sgl, bool pd)
{
	ushort		adcmd[3];
	short		count = 2;

	//	Setup the command word we send to the Max146 which is a combination of
	//	selector bits that pick the channel, mode, and clocking.
	adcmd[0] = adcmd[1] =
			 		(sgl	? (Max146SglChSel[chan] | Max146SGL)
							: (Max146DifChSel[chan] | Max146DIF))
				|	(uni	? Max146UNI : Max146BIP)
				|	Max146CMD;

	adcmd[0] |= Max146EXT;
	if (pd)
	{
		adcmd[1] |= Max146PDFast;
		adcmd[count++] = 0;
	}
	else
		adcmd[1] |= Max146EXT;
		
	//	Perform a QSPI transaction with two transfers, the first sets up the next
	//	conversion, and the second value clocks out the previous channel results.
	//	We don't set the QPBASync bit in the command count so the function will
	//	wait for the conversion to complete before returning.

	QPBTransact(QPB_MAX146_Slot, 0, count, adcmd);
	//	The rcvData array contains the real result in its second word since word
	//	one is the result of some previous conversion.

	return QPB_MAX146_Slot->dev->rcvData[1];
}

/****************************************************/

float Max146Battery()
{
	short sample;
	
	sample = Max146Sample(AD0, true, true, false);	// Chan, Uni, Sgl, Pd
	return ((float)sample) / BATTERY_CAL;
}

/***************************************************/

float Max146BackupBat()
{
	short sample;
	
	sample = Max146Sample(AD1, true, true, false);	// Chan, Uni, Sgl, Pd
	return ((float)sample) / BACKUP_BAT_CAL;
}

/***************************************************/

void SPI4MOTControl(ushort device, char on_off)
{
	if(on_off) g_SPI4MOT_byte |= device;
	else g_SPI4MOT_byte &= ~device;

	QPBTransact(QPB_SPI4MOT_Slot, 0, 1, &g_SPI4MOT_byte);
}



///////////////////////////////////////////////////
///////////////////////////////////////////////////
// Miscellaneous Routines
///////////////////////////////////////////////////
///////////////////////////////////////////////////

void GetDateAndTime(char *dateTimeStr)
{
	unsigned long tmp;
	int year;
	RTCtm * myTm = NULL;
	RTCtime(&tmp);
	myTm = RTClocaltime(&tmp);
	year = myTm->tm_year;
	if(year > 99)
		year -= 100;
	if(dateTimeStr != NULL)
		snprintf(dateTimeStr, DATETIME_SIZ, "%02d%02d%02d%02d%02d", (myTm->tm_mon) + 1, myTm->tm_mday, myTm->tm_hour, myTm->tm_min, year);
}

/****************************************************/

void GetBatteryLevel(char *battStr)
{
	if(battStr != NULL)
	{
		snprintf(battStr, 5, "%04.1f", Max146Battery());
	}
}

/****************************************************/

void SleepWithBreak()
{
	// Shut down all power
	SPI4MOTControl(ALL_SPI4_OFF, ON);
	
	PinClear(TPU4);				// Grab Sensor Power
	PinClear(TPU5);				// Comport Power (not used in H2O)
	PinClear(TPU6);				// 5V and 12V Stir (MAIN)

	// Install the interrupt handlers that will break us out from keystroke.
	IEVInsertAsmFunct(Irq4RxISR, level4InterruptAutovector);
	IEVInsertAsmFunct(Irq4RxISR, spuriousInterrupt);

    fprintf(stderr, "\n\nPress any key to wake from sleep...");
	
	SCITxWaitCompletion();		// let any pending UART data finish
	EIAForceOff(true);			// turn off the RS232 driver
	ShutdownTPU();				// turn off TPU
	CFEnable(false);			// turn off the CompactFlash card
	PinBus(IRQ4RXD);			// make it an interrupt pin
		
	LPStopCSE(FullStop);		// we're here until interrupted
	
	EIAForceOff(false);			// turn on the RS232 driver
	CFEnable(true);				// turn on the CompactFlash card
	ciflush();					// discard any garbage characters
	InitializeTPU();
	
	fprintf(stderr, " Awake\n");	// tell 'em we're back		
}

/***************************************************/

void DelaySecs(int secs, bool allowKBBreak)
{
	int x;
	
	for(x=0;x<secs;x++) {
		RTCDelayMicroSeconds(1000000);
		if(allowKBBreak && kbhit())
			break;
	}
}

/***************************************************/

bool DelayPC104(short type, int timeout)
{
	FILE *pc104_fp = fopen("PC104.txt", "a");
	time_t start = RTCGetTime(NULL, NULL);
	time_t temp_time;
	struct tm *timePtr;
	int state = 0;
	int outer = 0;
	char c;
	bool good = false;
	
	if(type == BOOT)
	{
		while(RTCGetTime(NULL, NULL) < start + timeout && !kbhit())
		{
			if(tgetq(com1))
			{
				c = tgetc(com1);
				if(state == 0 && c == 's')
					state = 1;
				else if(state == 1 && c == 'h')
					state = 2;
				else if(state == 2 && c == '-')
					state = 3;
				else if(state == 3 && c == '2')
					state = 4;
				else if(state == 4 && c == '.')
					state = 5;
				else if(state == 5 && c == '0')
					state = 6;
				else if(state == 6 && c == '5')
					state = 7;
				else if(state == 7 && c == 'b')
					state = 8;
				else if(state == 8 && c == '#')
				{
					temp_time = RTCGetTime(NULL, NULL);
					timePtr = localtime(&temp_time);
					if(pc104_fp != NULL)
					{
						fprintf(pc104_fp, "PC104 Booted Successfully    %s", asctime(timePtr));
						fclose(pc104_fp);
					}
					good = true;
				}
				else
					state = 0;
			}
		}
	}
	else if(type == PICTURE)
	{
		while(RTCGetTime(NULL, NULL) < start + timeout && !kbhit())
		{
			if(tgetq(com1))
			{
				c = tgetc(com1);
				
				if(outer == 0)
				{
					if(state == 0 && c == 'D')
						state = 1;
					else if(state == 1 && c == 'o')
						state = 2;
					else if(state == 2 && c == 'w')
						state = 3;
					else if(state == 3 && c == 'n')
						state = 4;
					else if(state == 4 && c == 'l')
						state = 5;
					else if(state == 5 && c == 'o')
						state = 6;
					else if(state == 6 && c == 'a')
						state = 7;
					else if(state == 7 && c == 'd')
					{
						if(defaults.flood_on)
							mFloodLightOff();
						outer = 1;
						state = 0;
					}
					else
						state = 0;
				}
				else if(outer == 1)
				{
					if(state == 0 && c == 's')
						state = 1;
					else if(state == 1 && c == 'h')
						state = 2;
					else if(state == 2 && c == '-')
						state = 3;
					else if(state == 3 && c == '2')
						state = 4;
					else if(state == 4 && c == '.')
						state = 5;
					else if(state == 5 && c == '0')
						state = 6;
					else if(state == 6 && c == '5')
						state = 7;
					else if(state == 7 && c == 'b')
						state = 8;
					else if(state == 8 && c == '#')
					{
						temp_time = RTCGetTime(NULL, NULL);
						timePtr = localtime(&temp_time);						
						if(pc104_fp != NULL)
						{
							fprintf(pc104_fp, "PC104 Picture Taken          %s", asctime(timePtr));
							fclose(pc104_fp);
						}
						good = true;
					}
					else
						state = 0;
				}
			}
		}
	}
	else if(type == SHUTDOWN)
	{
		while(RTCGetTime(NULL, NULL) < start + timeout && !kbhit())
		{
			if(tgetq(com1))
			{
				c = tgetc(com1);
				if(state == 0 && c == 'P')
					state = 1;
				else if(state == 1 && c == 'o')
					state = 2;
				else if(state == 2 && c == 'w')
					state = 3;
				else if(state == 3 && c == 'e')
					state = 4;
				else if(state == 4 && c == 'r')
					state = 5;
				else if(state == 5 && c == ' ')
					state = 6;
				else if(state == 6 && c == 'd')
					state = 7;
				else if(state == 7 && c == 'o')
					state = 8;
				else if(state == 8 && c == 'w')
					state = 9;
				else if(state == 9 && c == 'n')
					state = 10;
				else if(state == 10 && c == '.')
				{
					temp_time = RTCGetTime(NULL, NULL);
					timePtr = localtime(&temp_time);
					if(pc104_fp != NULL)
					{
						fprintf(pc104_fp, "PC104 Shutdown Successfully  %s\n", asctime(timePtr));
						fclose(pc104_fp);
					}
					good = true;
				}
				else
					state = 0;
			}
		}
	}
	
	// Skip the rest if good
	if(good)
		return true;
		
	// Something went wrong...
	temp_time = RTCGetTime(NULL, NULL);
	timePtr = localtime(&temp_time);
	if(type == BOOT && pc104_fp != NULL)
		fprintf(pc104_fp, "PC104 Failed to Boot Up      %s", asctime(timePtr));
	else if(type == PICTURE && pc104_fp != NULL)
		fprintf(pc104_fp, "PC104 Failed to Take Picture %s", asctime(timePtr));
	else if(pc104_fp != NULL)
		fprintf(pc104_fp, "PC104 Failed to Shutdown     %s\n", asctime(timePtr));
	
	if(pc104_fp != NULL)
		fclose(pc104_fp);
	
	return false;
}

/**********************************************************/

void DelayCycles(long num)
{
	long i = 0;
	while(i < num)
		i++;
}

/******************************************************************************\
**	Irq4RxISR			Interrupt handler for IRQ4 (tied to CMOS RxD)
**	
**	This single interrrupt service routine handles both the IRQ4 interrupt
**	and the very likely spurious interrupts that my be generated by the
**	repeated asynchronous and non-acknowledged pulses of RS-232 input.
**	
**	The handler simply reverts IRQ4 back to input (to prevent further level
**	sensative interrupts) and returns. It's assumed the routine that set this
**	up is just looking for the side effect of breaking the CPU out of STOP
**	or LPSTOP mode.
**	
**	Note that this very simple handler is defined as a normal C function
**	rather than an IEV_C_PROTO/IEV_C_FUNCT. We can do this because we know
**	(and can verify by checking the disassembly) that is generates only
**	direct addressing instructions and will not modify any registers.
\******************************************************************************/
void Irq4RxISR()
{
	PinIO(IRQ4RXD);		// 31 // /IRQ4 (tied to Rx)
	RTE();
}