//tokenring.c
//
//This is a fully functional Token Ring node code.  Send and Receive messages
//with other nodes
//
//Derek Marston and Peter Susi
//5/07/03

#include "common.h"
#include "iob32.h" //all IO and memory names

//	Authorize interrupts.

#define cli()	_asm("andcc #$EF")

#define SIZE	21	/* buffer size */
#define MSGLEN  16	/* max message length */
#define TDRE	0x80	/* transmit ready bit */
//#define OURADDR 'A'	//our node address
#define OURADDR '@'
#define BROADCAST '0'	//broadcast address

char KBbuff[SIZE];	/* reception buffer */
char RxTokbuff[SIZE];   /* receive token buffer */
char RdyTokbuff[SIZE];  /* to send token buffer */
char Displaybuff[SIZE]; /* display buffer */

char *kbptr;		//keyboard buffer pointer
char *rxptr;		//receive token buffer pointer
char *rdyptr;		//send token buffer pointer
char *dispptr;		//display buffer pointer

int RecFlag;
int Data2SendFlag;
int DispFlag;


void main(void)
{

	Init();
	ConfigUSART();
	ConfigSCI();
	cli();


	for(;;)
	{
		if (RecFlag == 0xFF)
			ProcessFree();
		if (RecFlag == 0xBF)
			ProcessBusy();
		if (DispFlag)
			Display();
	}
}

//Initializes ports and pointers to buffers
void Init()
{
	DDRA = 0x8F;
	/*
	pin 0 = USART reset
	pin 1 = Read'
	pin 2 = Write'
	pin 3 = command/data'

	pin 4 = TxRDY
	pin 5 = RxRDY
	pin 6 = unused

	pin 7 = output square wave for debug purposes

	*/

	DDRB = 0xFF; //PORTB is command/status/data register of USART
	//set to output to initialize USART

	//initialize pointers
	kbptr = &KBbuff[0];
	rxptr = &RxTokbuff[0];
	rdyptr = &RdyTokbuff[0];
	dispptr = &Displaybuff[0];

	//initialize static members in busy token buffer
	RdyTokbuff[0] = 0xAA;
	RdyTokbuff[1] = 0xBB;
	RdyTokbuff[2] = OURADDR;

	RdyTokbuff[SIZE-2] = 0x0D;
	RdyTokbuff[SIZE-1] = 0xAA;

	//initialize flags
	RecFlag = 0;
	Data2SendFlag = 0;
	DispFlag = 0;
}

/*Configure 8251 USART for:
;		Asyncronous serial data
;		Baud Rate Factor = 16
;		8Bit data, no parity
;		1 stop bit
;		Enable transmitter
*/
void ConfigUSART()
{
	PORTB = 0x4E; //send initialization code to USART

	PORTA = 0x07; //set pin 0 high for USART hardware reset

	wait();

	PORTA = 0x06; //end reset pulse

	PORTA = 0x0E; //set USART ready for COMMAND
	PORTA = 0x0A; //Set USART ready for WRITE 
	PORTA = 0x0E; //end write pulse

	wait();

	PORTB = 0x05; //send second byte of initialization code

	PORTA = 0x0E; //set USART ready for COMMAND
	PORTA = 0x0A; //Set USART ready for WRITE 
	PORTA = 0x0E; //end write pulse

	wait();

	PORTA = 0x06; //set USART to idle mode}
}

//delay a few clock cycles
//
void wait(void)
{
	int count;
	for (count = 0; count < 4; count++)
	{}
}

//copy contents of one buffer to another
//
void CopyBuff(char *src, char *dest, int numbytes)
{	
	int c;

	for (c = 0; c < numbytes; c++)
	{
		*dest++ = *src++;
	}
}

//display the contents of the display buffer to the hyperterminal window

void Display()
{
	DispFlag = 0x00;

	for (dispptr = &Displaybuff[0]; *dispptr != 0x0D; dispptr++)
		CharOut(*dispptr);

	CharOut(0x0D);

}

//Configures port for Asynchronous serial 8-bit data, no parity, 1 stop bit
//enable the transmitter and receiver
void ConfigSCI()
{
	
	SC0CR1 = 0x00; 	
 		// enable Tx, Rx and RxInterrupt

	SC0CR2 = 0x2C;

	SC0BDL = 0x34;	//BR divisor for 9600 BPS

}

//Waits for the transmitter to be ready (until TDRE bit in the Status Register is 1)
//then writes the contents of the passed parameter (one byte) to the Data Register
void CharOut(char tosend)
{

	while (!(SC0SR1 & TDRE))  //TDRE is defined above
	{}	

	SC0DRL = tosend;
}	

//Outputs text messages to the Hyperterminal Screen for debugging purposes
//
void StrOut(char *msg, int len)
{
	int c;
	for (c = 0; c < len; c++)
		CharOut(msg[c]);
	CharOut(0x0D);

//	CopyBuff(msg, &DisplayBuff[0], len);
//	DispFlag = 0xFF;


}

// Reads last byte from the Data register and returns it.
//
char GetKey()
{
	return SC0DRL;
}

//Wait for one byte to arrive at the USART and return it.
char Receive()
{
	char DATA;

	DDRA = 0x8F;

	while (!(PORTA & BIT5))	//wait for RxRdy signal
	{
		PORTA = 0x86;
	}
	PORTA = 0x06;

	DDRB = 0x00; 	//set Port B to input
	PORTA = 0x84;	//Pulse READ command for USART

	DATA = PORTB;

	PORTA = 0x06;	//end READ pulse
	return DATA;  //get data

}

//Transmit toTx via external USART (connected to PortB)
void Transmit(char toTx)
{

	while (!(PORTA & BIT4))  //waits until USART is ready to transmit
	{}

	DDRB = 0xFF;  //set PORTB to output on all pins

	PORTB = toTx;  //set data on USART's data bus via portB
	

	PORTA = 0x02; //Set USART ready for WRITE data
	PORTA = 0x06; //end write pulse

}

//send a Free Token (AA FF AA)
void SendFree(void)
{
	Transmit(0xAA);
	Transmit(0xFF);
	Transmit(0xAA);
}

//Send a busy token according to data in RdyBuff

void SendBusy()
{
	Data2SendFlag = 0x00;   //clear busy token flag

	for (rdyptr=&RdyTokbuff[0]; rdyptr < &RdyTokbuff[SIZE]; rdyptr++)
		Transmit(*rdyptr);
}

void EchoBusy()
{

	for (rxptr=&RxTokbuff[0]; rxptr < &RxTokbuff[SIZE]; rxptr++)
		Transmit(*rxptr);

}

// Interrupt Routine for recieving Hyperterminal bytes
// ie messages typed into the keyboard
//
@interrupt void _SCIRx_ISR(void)
{
        SC0SR1; // read Status Reg to clear interrupt 	

	*kbptr = SC0DRL;   // put char in keyboard buffer

	SC0DRL = *kbptr;    // Echo back byte


	if (*kbptr == 0x1B)
		SendFree();  //send a free token when Esc is pressed

	else if (*kbptr == 0x08)
		kbptr--;    //don't transmit backspace character
			   // or previous character

	else if (++kbptr >= &KBbuff[MSGLEN] || *(kbptr-1) == 0x0D)
	{
		kbptr = &KBbuff[0];
		CopyBuff(&KBbuff[0], &RdyTokbuff[3], MSGLEN);
		Data2SendFlag = 0xFF;
		CharOut(0x0D);  // see on screen that message was sent.
		CharOut('>');
	}


}


//Interrupt Routine triggered on RxRdy (tied to IRQ, or PE1)
// to receive network data.
//
@interrupt void _IRQ_ISR(void)
{
	CollectToken();
}

void CollectToken()
{
	RecFlag = 0x00;
	rxptr = &RxTokbuff[0];		//initialize pointer


	if ((*rxptr = Receive()) == 0xAA) //get first byte, see if it's AA
	{
		rxptr++;
		if ((*rxptr = Receive()) == 0xFF) //see if second byte is FF
		{
			rxptr++;

			if ((*rxptr = Receive()) == 0xAA) //if third byte is AA
			{
				RecFlag = 0xFF; // then it's a valid free token
				return;
			}
		}
			
		else if (*rxptr == 0xBB) //if second byte is not FF, 
		{			//see if it is BB
			rxptr++;
			for (;rxptr < &RxTokbuff[SIZE]; rxptr++)
			{
				*rxptr = Receive();
			} // get the rest of the bytes

			if (*(rxptr-1) == 0xAA)
			{
				RecFlag = 0xBF;
			}

			return;
		}	

/*		else if (*rxptr == 0xAA) //if second byte is AA
			StrOut("Error: Second byte of token is AA",33);

		else
			//StrOut("Not FF or BB", 12);
			CharOut(*rxptr);
*/

	}

	return;
}

//Process a Free token:  echo a free if there is no data to send,
//			 send a busy token if there is.

void ProcessFree()
{
	RecFlag = 0x00;		//clear the received data flag

	if (Data2SendFlag == 0xFF)
		SendBusy();
	else
		SendFree();

	return;

}
//Process a Busy Token:  Display the message if it is for us or broadcast
//			 pass on a busy token for someone else
void ProcessBusy()
{
	RecFlag = 0x00;		//clear the received data flag

	//see if token is TO us
	if (RxTokbuff[3] == OURADDR || RxTokbuff[3] == BROADCAST)
	{
		CopyBuff(&RxTokbuff[2], &Displaybuff[0], MSGLEN+2);
		DispFlag = 0xFF;
	}  
	
	//see if token is FROM us
	if (RxTokbuff[2] == OURADDR)
	{
//		StrOut("Message Sent", 12);  //put messages in display buffer later

		//take busy token off and recirculate free
		SendFree(); 		  
	}
	else  //if token is not from us, keep it circulating
	{
		EchoBusy();
	}
}

//Generates a square wave on all pins of Port A.  Use for debugging.
//
void square(void)
{
	DDRA = 0xFF;

	for(;;) //generate a square wave on all pins of Port A
	{
		PORTA = 0x00;	//turn bit off
		wait();

		PORTA = 0xFF; //(PORTA & BIT7);	//turn bit on
		wait();
	}	

}
