/*******************************************************************************
**	max3100.c
*******************************************************************************/

// Thomas P. Sullivan
// Enertech Consultants
// 11-18-98

// This software demonstrates using the Maxim 3100 UART with the QSPI of the 68338
//	found on the Persistor Instruments Inc. CF1.  The software is provided for demonstration
// purposes ONLY and no warranties are expressed or implied.  The software is provide AS IS. 
// Having said that, I hope you find it useful.

//	The hardware needed to support this application is a CF1 and it's prototyping board.
// Also, wired to the prototyping board are two MAX3100 UARTS (I used DIPS).  Each is connected
//	to all the QSPI connections (i.e. MISO,MOSI,SCK).  For the chip selects, one MAX3100 is connected to
// PCS0 (and we'll call it Port 0) and the other is connected to PCS1 (and we'll call it Port 1).
//	The -IRQ line from both MAX3100's is connected to the -IRQ2 line on the CF1 and pulled up 
//	to VREG through a 1 megOhm resistor.  The -SHDN line from both MAX3100's is connected to the 
//	CF1 pin CTS14A (if you need this pin then change it to another).  The -SHDN line from
//	the MAX3100's can also be simply pulled-up if you do not need any control over this signal.
//	The crystal frequency used for the MAX3100's is 3.6864 MHz.  A slower crystal can also be
//	used.  If a slower crystal is used then the configuration word will have to be modified.
//	For this application using a 3.6864 MHz crystal, the baud rate for both parts is set at
//	9600 (8N1).  To use this software, wire the board as described and connect TX from one 
//	MAX3100 to the RX of the other (2 jumpers).  THIS IS JUST FOR TESTING USING THIS SOFTWARE.

// The output of the MAX3100 is a digital signal.  To change these signal to and from RS-232
//	levels, use a chip like the MAX3223 (the MAX3100 is JUST A UART).

// Compile, load and run this software.  The software sends a byte to port 1 using port 0 and
//	sends a different byte to port 0 using port 1 (they play catch).  The software supports interrupt
// driven buffered receive and polled transmit.


//
// This schematic is probably viewed best with a fixed font.
//
//
//
//	                                         +------+-+------+
//	                                        1|      | |      |14
//	       MOSI  ---------------*------------+ Din   -   Vcc +----  VREG
//	                            |           2|               |13
//	       MISO  ------------*--[------------+ Dout       Tx +-----------------------+
//	                         |  |           3|               |12                     |
//	        SCK  ---------*--[--[------------+ SCLK       Rx +--------------------+  |
//	                      |  |  |           4|               |11                  |  |
//	       PCS0  ---------[--[--[------------+ -CS      -RTS +                    |  |
//	                      |  |  |           5|               |10                  |  |
//	      -IRQ2  ------*--[--[--[------------+ -IRQ     -CTS +----- VREG          |  |
//	                   |  |  |  |           6|               |9          22pf     |  |
//	                   |  |  |  |  *---------+ -SHDN      X1 +-----+-----||--+    |  |
//	                   |  |  |  |  |        7|               |8   ###        |    |  |
//	                   |  |  |  |  |   +-----+ GND        X2 +-----+-----||--+    |  |
//	                   |  |  |  |  |   |     |               |  Crystal  22pf|    |  |
//	                   |  |  |  |  |   |     +---------------+   3.6864      |    |  |
//	                   |  |  |  |  |  GND         MAX3100         MHz       GND   |  |
//	                   |  |  |  |  |                                              |  |
//	                   |  |  |  |  |                                              |  |
//	                   |  |  |  |  |                                              |  |
//	                   |  |  |  |  |         +------+-+------+                    |  |
//	                   |  |  |  |  |        1|      | |      |14                  |  |
//	                   |  |  |  +--[---------+ Din   -   Vcc +----  VREG          |  |
//	                   |  |  |     |        2|               |13                  |  |
//	                   |  |  +-----[---------+ Dout       Tx +--------------------+  |
//	                   |  |        |        3|               |12                     |
//	                   |  *--------[---------+ SCLK       Rx +-----------------------+
//	                   |           |        4|               |11
//	       PCS1  ------[-----------[---------+ -CS      -RTS +
//	                   |           |        5|               |10
//	                +--*-----------[---------+ -IRQ     -CTS +----- VREG
//	                |              |        6|               |9          22pf
//	                /           +--+---------+ -SHDN      X1 +-----+-----||--+
//	                \           |           7|               |8   ###        |
//	                /           /      +-----+ GND        X2 +-----+-----||--+
//	        1 MEG   \           \      |     |               |  Crystal  22pf|
//	                /           /      |     +---------------+   3.6864      |
//	                \           \     GND         MAX3100         MHz       GND
//                 |           /
//	       VREG  ---+           \
//	                            |
//	                    VREG ---+




#include	<cf1bios.h>		// Persistor CF1 System and I/O Definitions
#include	<cf1pico.h>		// Persistor CF1 PicoDOS Definitions  

#include	<assert.h>
#include	<ctype.h>
#include	<errno.h>
#include	<float.h>
#include	<limits.h>
#include	<locale.h>
#include	<math.h>
#include	<setjmp.h>
#include	<signal.h>
#include	<stdarg.h>
#include	<stddef.h>
#include	<stdio.h>
#include	<stdlib.h>
#include	<string.h>
#include	<time.h>

#include "Max3100.h"

//	Global variables (well, global in scope right John!?)
static QPB	*QPB_Slot0 = NULL;	//NULL to start
static QPB	*QPB_Slot1 = NULL;	//NULL to start

ushort ReadData = 0x0000;
ushort ReadConfig = 0x4000;
ushort Config = 0xC40B;				//Just receive interupts on
ushort ConfigTRIntON = 0xCC0B;	//Receive AND transmit interupts on
ushort WriteData;

int GnMoreToSend0;
int GnMoreToSend1;

unsigned char *TopOfRecBuffer0, *HeadRecBuffer0, *TailRecBuffer0;
unsigned char *TopOfRecBuffer1, *HeadRecBuffer1, *TailRecBuffer1;

unsigned char *TopOfXmitBuffer0, *PtrXmitBuffer0;
unsigned char *TopOfXmitBuffer1, *PtrXmitBuffer1;

//
// Interrupt Handler
//

IEV_C_PROTO(Max3100InterruptISR);
IEV_C_FUNCT(Max3100InterruptISR)
{
	#pragma unused(ievstack)

	ushort RecDataPort0;
	ushort RecDataPort1;

// ********************************* Receive *******************************

//	Read data from Port0
	MaxSendPort0(&ReadData,1);
	RecDataPort0 = QPB_Slot0->dev->rcvData[0];
//	Read data from Port1
	MaxSendPort1(&ReadData,1);
	RecDataPort1 = QPB_Slot1->dev->rcvData[0];

//	For Port 0
//	If R (bit 15) == 1
	if(RecDataPort0 & 0x8000)
		{
//		Move lower 8 bits to the receive buffer
		AppendBuffer0((unsigned char) (RecDataPort0 & 0x00FF));
//		printf("RecPort0 [%04x]\n",RecDataPort0);
		}
//	EndIf

//	For Port 1
//	If R (bit 15) == 1
	if(RecDataPort1 & 0x8000)
		{
//		Move lower 8 bits to the receive buffer
		AppendBuffer1((unsigned char) (RecDataPort1 & 0x00FF));
//		printf("RecPort1 [%04x]\n",RecDataPort1);
		}
//	EndIf

// ********************************* Transmit *******************************

//	If (there is another character waiting to be sent)
	if(GnMoreToSend0)
		{
//		If T (bit 14) == 1
		if(RecDataPort0 & 0x4000)
			{
//			Get the next character from the output buffer
//			Send the character
			if(mPutcPort0((int) *PtrXmitBuffer0))
				{
				PtrXmitBuffer0++;
				GnMoreToSend0 = GnMoreToSend0 - 1;

// 			Send config word to turn off transmit interupts (leaving the receive interupt ON)
				if(GnMoreToSend0 <= 0)
					{
					while(!MaxSendPort0(&Config,1))
						;
					}
				}
			}
//		EndIf
		}
//	EndIf


//	If (there is another character waiting to be sent)
	if(GnMoreToSend1)
		{
//		If T (bit 14) == 1
		if(RecDataPort1 & 0x4000)
			{
//			Get the next character from the output buffer
//			Send the character
			if(mPutcPort1((int) *PtrXmitBuffer1))
				{
				PtrXmitBuffer1++;
				GnMoreToSend1 = GnMoreToSend1 - 1;

// 			Send config word to turn off transmit interupts (leaving the receive interupt ON)
				if(GnMoreToSend1 <= 0)
					{
					while(!MaxSendPort1(&Config,1))
						;
					}
				}
			}
//		EndIf
		}
//	EndIf
		//RTCDelayMicroSeconds (10000);

}

//******************************************************************************
//	main
//******************************************************************************
void main(void)
	{
	int c;
	char cMessage0[2048];
	char cMessage1[2048];

//	To handle our interupts from the MAX3100 we install this handler
	IEVInsertCFunct(&Max3100InterruptISR, (short) level2InterruptAutovector);

	ENABLE3100;

//	Buffers point nowhere to start
	TopOfRecBuffer0 = NULL;
	HeadRecBuffer0 = NULL;
	TailRecBuffer0 = NULL;
	TopOfRecBuffer1 = NULL;
	HeadRecBuffer1 = NULL;
	TailRecBuffer1 = NULL;

	TopOfXmitBuffer0 = NULL;
	PtrXmitBuffer0 = NULL;
	TopOfXmitBuffer1 = NULL;
	PtrXmitBuffer1 = NULL;

	GnMoreToSend0 = GnMoreToSend1 = 0;

//	Allocate memory for the buffers
	TopOfRecBuffer0 = malloc(RECBUFFSIZE * sizeof(char));
	if(TopOfRecBuffer0 == NULL)
	{
		printf("Cannot allocate memory for receive buffer 0\n\n");
		BIOSResetToPicoDOS();
	}
	else
	{
		FlushBuffer0();	//Resets everthing
	}

	TopOfRecBuffer1 = malloc(RECBUFFSIZE * sizeof(char));
	if(TopOfRecBuffer1 == NULL)
	{
		printf("Cannot allocate memory for receive buffer 1\n\n");
		BIOSResetToPicoDOS();
	}
	else
	{
		FlushBuffer1();	//Resets everthing
	}

	TopOfXmitBuffer0 = malloc(XMITBUFFSIZE * sizeof(char));
	if(TopOfXmitBuffer0 == NULL)
	{
		printf("Cannot allocate memory for transmit buffer 0\n\n");
		BIOSResetToPicoDOS();
	}
	else
	{
		PtrXmitBuffer0 = TopOfXmitBuffer0;
	}


	TopOfXmitBuffer1 = malloc(XMITBUFFSIZE * sizeof(char));
	if(TopOfXmitBuffer1 == NULL)
	{
		printf("Cannot allocate memory for transmit buffer 1\n\n");
		BIOSResetToPicoDOS();
	}
	else
	{
		PtrXmitBuffer1 = TopOfXmitBuffer1;
	}

//	I send the configuration command twice because it seemed that sometimes
// once wasn't enough.  It may not be needed in reality but it doesn't hurt.
	MaxSendPort0(&Config,1);
	MaxSendPort0(&Config,1);
	MaxSendPort1(&Config,1);
	MaxSendPort1(&Config,1);

//	Enable -IRQ2 as a bus function (normally it is an I/O pin and by default it is an input)
	PIOBusFunct(41);

	FillAndSendBuffer0(cMessage0,strlen(cMessage0));
//	printf("Sent %s to Port 1 via Port 0\n",cMessage0);
	FillAndSendBuffer1(cMessage1,strlen(cMessage1));
//	printf("Sent %s to Port 0 via Port 1\n",cMessage1);

//	We'll do this for a very long time
	while(THE_EARTH_IS_ORBITING_THE_SUN)
	{
		printf("GnMoreToSend0: %d  GnMoreToSend1: %d\n",GnMoreToSend0,GnMoreToSend1);
		c = GetNextBuffer0();
		if(c != 0x00FF)
			printf(" PORT 0: %c\n",(char)(c & 0x00FF));
		c = GetNextBuffer1();
		if(c != 0x00FF)
			printf(" PORT 1: %c\n\n",(char)(c & 0x00FF));

//		RTCDelayMicroSeconds (1000000);
	}

//	Assuming we remove the infinite loop at some point, this is how to disable the MAX3100
//	using an I/O line on the CF1 to force a shutdown.
	DISABLE3100;

	BIOSResetToPicoDOS();	// full reset, but jump to 0xE10000 (PicoDOS)

	}	//main

//
// Talk to the QPB for port 0
//
bool MaxSendPort0(ushort *uCmd, ushort unCount)
	{

	QPBInit(true);	// Makes the QSPI in the 338 a master on SPI

	QPB_Slot0 = QPBInitSlot(&Max3100DevStruct0);	//Register our little device driver
	QPB_Slot0->dev->xfrCount = unCount;

	if(QPBLockSlot(QPB_Slot0) == true)	//Lock that puppy down
		{
		QPBTransact(QPB_Slot0, 0, QPB_Slot0->dev->xfrCount, uCmd);	// Do our transfers
		QPBUnlockSlot(QPB_Slot0);			//Un-lock that puppy
		return true;
		}
	else
		return false;
	
	}

//
// Talk to the QPB for port 1
//
bool MaxSendPort1(ushort *uCmd, ushort unCount)
	{

	QPBInit(true);	// Makes the QSPI in the 338 a master on SPI

	QPB_Slot1 = QPBInitSlot(&Max3100DevStruct1);	//Register our little device driver
	QPB_Slot1->dev->xfrCount = unCount;

	if(QPBLockSlot(QPB_Slot1) == true)	//Lock that puppy down
		{
		QPBTransact(QPB_Slot1, 0, QPB_Slot1->dev->xfrCount, uCmd);	// Do our transfers
		QPBUnlockSlot(QPB_Slot1);			//Un-lock that puppy
		return true;
		}
	else
		return false;
	
	}


bool  mPutcPort0(int c)
{
	ushort data;
	bool bReturn = false;

	if(MaxSendPort0(&ReadConfig,1))
	{
		data = QPB_Slot0->dev->rcvData[0];
		if(data & ((ushort) 0x4000))
		{
			data = WRITEDATAMASK | (ushort) c;
//			We also have to make sure that transaction for the write happens
			if(MaxSendPort0(&data,1))
			{
				bReturn = true;	// Meaning, we were able to send a character
			}
		}
	}

	return bReturn;
}


bool  mPutcPort1(int c)
{
	ushort data;
	bool bReturn = false;

	if(MaxSendPort1(&ReadConfig,1))
	{
		data = QPB_Slot1->dev->rcvData[0];
		if(data & ((ushort) 0x4000))
		{
			data = WRITEDATAMASK | (ushort) c;
//			We also have to make sure that transaction for the write happens
			if(MaxSendPort1(&data,1))
			{
				bReturn = true;	// Meaning, we were able to send a character
			}
		}
	}

	return bReturn;
}


//
// PPPP     OOO    RRRR    TTTTT    000
// P   P   O   O   R   R     T     0   0
// P   P   O   O   R   R     T     0   0
// PPPP    O   O   RRRR      T     0   0
// P       O   O   R R       T     0   0
// P       O   O   R  R      T     0   0
// P        OOO    R   R     T      000
//

//	
//	This is code to support a ring buffer.  Data is stored in the Head of the buffer and read out
//	from the Tail.  Attempts to read when no data is present (Head == Tail) return a -1.
//	
//				+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
//	Buffer	| H | e | l | l | o |   | W | o | r | l | d | . | H | e | l | l | o | ! |   |   |   |   |
//				+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
//							 ^
//							 |																					  ^
//							 |																					  |
//							 |																					  |
//							 |																					  |
//							 |																					  |
//							 +------  Tail																	  +------Head
//	
//	
//	
//	The pointers to maintain this structure are automatically updated so they don't fall
//	off the end of the world.
//	

//
//	Add a character to the head of the buffer
//
int AppendBuffer0(unsigned char c)
{
	ushort SaveSR;
//	Disable interupts 
	SaveSR = IEVSaveSRThenDisable();

	*HeadRecBuffer0 = c;
	HeadRecBuffer0++;

	if(HeadRecBuffer0 >= (char *)(TopOfRecBuffer0+RECBUFFSIZE))
	{
		HeadRecBuffer0 = TopOfRecBuffer0;
	}

//	Restore interupts to what they were
	IEVRestoreSavedSR(SaveSR);

	return c;
}

//
//	Get a character from the tail of the buffer
//

int GetNextBuffer0(void)
{
	ushort SaveSR;
	unsigned char c;

//	Disable interupts 
	SaveSR = IEVSaveSRThenDisable();

	if(TailRecBuffer0 == HeadRecBuffer0)
	{
		c = -1;
	}
	else
	{
		c = *TailRecBuffer0;

		TailRecBuffer0++;

		if(TailRecBuffer0 >= (char *)(TopOfRecBuffer0+RECBUFFSIZE))
		{
			TailRecBuffer0 = TopOfRecBuffer0;
		}
	}

//	Restore interupts to what they were
	IEVRestoreSavedSR(SaveSR);

	return (int ) c;
}

//
//	Well, we don't really flush anything we just reset all the pointers to the top!
//

void FlushBuffer0(void)
{
	ushort SaveSR;
//	Disable interupts 
	SaveSR = IEVSaveSRThenDisable();

	HeadRecBuffer0 = TailRecBuffer0 = TopOfRecBuffer0;

//	Restore interupts to what they were
	IEVRestoreSavedSR(SaveSR);
}
//
// PPPP     OOO    RRRR    TTTTT     1
// P   P   O   O   R   R     T      11
// P   P   O   O   R   R     T       1
// PPPP    O   O   RRRR      T       1
// P       O   O   R R       T       1
// P       O   O   R  R      T       1
// P        OOO    R   R     T     11111
//

//
// Most comments for this block of code are the same as the routines above for
//	port 0 so I was lazy and left them out.
//
int AppendBuffer1(unsigned char c)
{
	ushort SaveSR;

	SaveSR = IEVSaveSRThenDisable();

	*HeadRecBuffer1 = c;
	HeadRecBuffer1++;

	if(HeadRecBuffer1 >= (char *)(TopOfRecBuffer1+RECBUFFSIZE))
	{
		HeadRecBuffer1 = TopOfRecBuffer1;
	}

	IEVRestoreSavedSR(SaveSR);

	return c;
}

int GetNextBuffer1(void)
{
	ushort SaveSR;
	unsigned char c;

	SaveSR = IEVSaveSRThenDisable();

	if(TailRecBuffer1 == HeadRecBuffer1)
	{
		c = -1;
	}
	else
	{
		c = *TailRecBuffer1;

		TailRecBuffer1++;

		if(TailRecBuffer1 >= (char *)(TopOfRecBuffer1+RECBUFFSIZE))
		{
			TailRecBuffer1 = TopOfRecBuffer1;
		}
	}

	IEVRestoreSavedSR(SaveSR);

	return (int ) c;
}

void FlushBuffer1(void)
{
	ushort SaveSR;
	SaveSR = IEVSaveSRThenDisable();

	HeadRecBuffer1 = TailRecBuffer1 = TopOfRecBuffer1;

	IEVRestoreSavedSR(SaveSR);
}


int FillAndSendBuffer0(char *ucPtrSource, int nSize)
{
//	Set the pointer to the top of the transmit buffer
	PtrXmitBuffer0 = TopOfXmitBuffer0;

//	Copy
	strncpy(PtrXmitBuffer0, ucPtrSource, nSize);
	GnMoreToSend0 = nSize;

//	Send the first character
	if(mPutcPort0((int) *PtrXmitBuffer0))
	{
		PtrXmitBuffer0++;
		GnMoreToSend0 = GnMoreToSend0 - 1;
	}

// Send config word to turn on transmit interupts
	while(!MaxSendPort0(&ConfigTRIntON,1))
		;
	return GnMoreToSend0;
}

int FillAndSendBuffer1(char *ucPtrSource, int nSize)
{
//	Set the pointer to the top of the transmit buffer
	PtrXmitBuffer1 = TopOfXmitBuffer1;

//	Copy
	strncpy(PtrXmitBuffer1, ucPtrSource, nSize);
	GnMoreToSend1 = nSize;

//	Send the first character
	if(mPutcPort1((int) *PtrXmitBuffer1))
	{
		PtrXmitBuffer1++;
		GnMoreToSend1 = GnMoreToSend1 - 1;
	}

// Send config word to turn on transmit interupts
	while(!MaxSendPort1(&ConfigTRIntON,1))
		;
	return GnMoreToSend1;

}
