/*======'============================< System.c >==============================*/
//   
//    Title:      SYSTEM UNIT
//    Saved date: 11 Jun 2003 08:44:25
//    Written by: JAG
//    Project:    RMS101 SBC
//    Target:     LH79520
//    Compiler:   GNU GCC for ARM
//		Copyright:	Subject to the Revely Open Source License (see readme.txt)
//
// Code for system related functions such as keyboard and sound.
//
//	The keyboard drivers are currently non-interrupt driven and non-buffered.
// This keeps their usage as simple as possible.  Although the software drivers
// do not implement buffering, the UART's receive FIFO will buffer 16 bytes, or
// about 3 key presses.
//
//	Copyright:	Subject to the Revely Open Source License (see readme.txt)
//
// History : 	30-04-03 JAG Initial Release
//					22-05-03 JAG Added FIFO buffer to support kbhit and future
//									 interrupt based driver.	
//
/*============================================================================*/

#include <stddefs.h> 
#include <regs.h>
#include <system.h>
#include <keytable.h>
#include <rstring.h>
#include <uart1.h>

/*==============================< Defines >===================================*/

// Local Flags and Masks
#define  SCROLLON	   0x0001
#define	NUMLOCK		0x0002
#define  CAPSLOCK		0x0004
#define  SHIFTON		0x0008
#define  CTRLON		0x0010
#define  ALTON			0x0020

#define  SCROLLOFF	0xfffe
#define  NUMOFF		0xfffd
#define  CAPSOFF		0xfffb
#define  SHIFTOFF		0xfff7
#define  CTRLOFF		0xffef
#define  ALTOFF		0xffdf

#define BREAKCODE		0x0100
#define BREAKCODECLR	0xfeff
#define EXTCODE		0x0200
#define EXTCODECLR	0xfdff

// Misc defines
#define KEYBUFFSIZE	20

/*===========================< Unit Variables >===============================*/
BYTE bScanCode;
WORD wSysFlags;
WORD wSysVersion;				// Hardware/firmware version
BYTE KeyBuff[KEYBUFFSIZE];	

extern char str[20];			
WORD wKeyBuffHead;
WORD wKeyBuffTail;

/*======================< Local Function Prototypes >=========================*/
void KbSpecialCode(BYTE bCode);
void KbHandleBreakCode(BYTE bCode);
BYTE KbHandleExtCode(BYTE bC);
BYTE GetRawCode(void);
BYTE RawCodeReceived(void);
WORD KbGetVersion(void);
BYTE KbCodeAvail(void);
void KbSendCode(BYTE bCode);
void KbService(void);

/*==========================< Global Functions >==============================*/

/*-------------------------------< KbInit >-----------------------------------*/
void SysInit(void)
{
	BYTE bI, bC;
	
	// Set up Uart 0 to transfer keyboard data
	// Setup Port pins
	UARTMUX |= 0x0003;
	// Enable clock to Uart 0
	PERCLKCTRL &= 0xfffe;
	// Make sure internal clock source is selected
	PERCLKSEL &= 0xfffc;
	// Select Baud rate timing to 9600
	UART0IBRD = 0x60;
	UART0FBRD = 0;
			
	UART0LCR_H = 0x70;  		// 8 data bits, FIFO ON, No parity
	UART0CR = 0x0301;			// Enable UART to transmit and receive
	wSysFlags=0;
	
	// Set up PWM1 for sound
	PWM1_PRE = 4;				// PWM Clock = HCLK/8
	PERCLKCTRL &= 0x02ff;	// enable PWM1 clock
	PWM1_EN = 0;

	// Start RTC with 32kHz timebase
	PERCLKSEL |= 0x0180;		// 32Khz
	PERCLKCTRL &= 0xfdff;	// Enable
	
	// Get version info from keyboard controller
	//do
	wSysVersion = KbGetVersion();
	//while (!wSysVersion);
	
	// Init key buffer
	wKeyBuffHead=0; wKeyBuffTail=0;
	
}

/*--------------------------------< KbHit >-----------------------------------*/
BYTE kbhit(void)
{
	KbService();
	if (wKeyBuffHead != wKeyBuffTail)
		return TRUE;
	else
		return FALSE;
}

/*--------------------------------< getch >-----------------------------------*/
BYTE getch(void)
{
	BYTE bValid=0, bAscii;
			 
	while (!kbhit())
		KbService();
		
	bAscii = KeyBuff[wKeyBuffTail];
	
	if (++wKeyBuffTail == KEYBUFFSIZE)
		wKeyBuffTail=0;
	
	return bAscii;
}

/*--------------------------------< KbGets >----------------------------------*/
WORD KbGets(PSTR pStr, WORD wMaxLen)
{
	WORD wLen=0;
	
	// Get printable characters until we get to max len or a CR
	do
	{
		*pStr = getch();
	} while ((wLen < wMaxLen) && (*pStr++ != 0x0d));
	
	return wLen;
}

/*--------------------------------< KbScan >----------------------------------*/
BYTE KbScan(void)
{			
	return bScanCode;
}

/*--------------------------------< KbLeds >----------------------------------*/
void KbLeds(BYTE bLed)
{
	SysDelay(1);			// small delay for keyboard controller to change state
	KbSendCode(0xed);
	SysDelay(1);
	KbSendCode(bLed);
	// Deal with acks from this transaction
	SysDelay(3);
	if (KbCodeAvail())
		GetRawCode();		// get ACK
	SysDelay(5);
	if (KbCodeAvail())
		GetRawCode();		// get ACK
}

/*-----------------------------< SysGetVersion >------------------------------*/
WORD SysGetVersion(void)
{
	return wSysVersion;
}

/*--------------------------------< SoundOn >---------------------------------*/
void SoundOn(WORD wFreq)
{
	PWM1_TC = (6451250 / wFreq) - 1;
	PWM1_DC = PWM1_TC / 2;
	PWM1_EN = 1;
}

/*--------------------------------< SoundOn >---------------------------------*/
void SoundOff(void)
{
	PWM1_EN = 0;
}


/*===========================< Local Functions >==============================*/

/*-------------------------------< KbService >--------------------------------*/
void KbService(void)
{
	BYTE bValid=FALSE, bAscii, bKey;
	
	if (RawCodeReceived())
	{
		// Get scan-code byte from UART
		bScanCode = GetRawCode();
		
		// Are we expecting a Break Code ?
		if (wSysFlags & BREAKCODE)
		{
			//UartSend("Got Break ");
			KbHandleBreakCode(bScanCode); 	
			wSysFlags &= BREAKCODECLR;
			return;
		}
		
		// Are we expecting an Extended Code ?
		if (wSysFlags & EXTCODE)
		{
			//UartSend("Got Ext ");
			if (KbHandleExtCode(bScanCode) == TRUE)
				bValid = TRUE;	 	
			wSysFlags &= EXTCODECLR;
		}
		else
		{		
			switch (bScanCode)
			{
								// Discard various special codes (ACK etc).
				case 0xfa:	break;
				case 0xfe:
				case 0xff:	break;
				case 0xf0:	// Break code
								wSysFlags |= BREAKCODE;
								break;
				case 0xe0:	// Extended Code ?
								wSysFlags |= EXTCODE;
								break;
				default:		if (StdCodes[bScanCode] != 0) 	// Normal ?
									bValid = TRUE; 
								else
									KbSpecialCode(bScanCode);		// check for specials
			}
		}
	}
	if (bValid)		// something to put in buffer ?
	{
		//UartSend("Got Keypress ");
		bAscii = StdCodes[bScanCode];

		if (bAscii & 0x80)		// MSB set ?
		{
		 	if (wSysFlags & NUMLOCK)
		 		bKey = ShiftCodes[bAscii & 0x7f];	 	// Num lock down so translate
		}
		else
			bKey = bAscii;
			
		if (wSysFlags & SHIFTON)
			bKey = ShiftCodes[bAscii];						// Shift translates all keys
			
		if ((wSysFlags & CAPSLOCK) && (bAscii >= 0x61) && (bAscii <= 0x7a))
			bKey = ShiftCodes[bAscii];						// Caps lock just some
			
		if ((wSysFlags & NUMLOCK) && (bAscii & 0x80))
			bKey = ShiftCodes[bAscii & 0x7f];
	
		KeyBuff[wKeyBuffHead] = bKey;						// Into buffer
		if (++wKeyBuffHead == KEYBUFFSIZE)
			wKeyBuffHead=0;
	}
}

/*-----------------------------< KbSpecialCode >------------------------------*/
void KbSpecialCode(BYTE bCode)
{
	BYTE bCLed = FALSE;
	
	switch (bCode)
	{
		case 0x58:	wSysFlags ^= CAPSLOCK;		// Toggle Caps Lock
						bCLed = TRUE;
						break;
		case 0x77:	wSysFlags ^= NUMLOCK;		// Toggle Num Lock
						bCLed = TRUE;
						break;
		case 0x7e:	wSysFlags ^= SCROLLON;		// Toggle Scroll lock
						bCLed = TRUE;
						break;
		case 0x59:	
		case 0x12:	wSysFlags |= SHIFTON;		// Left or Right Shift ON
						break;
		case 0x11:	wSysFlags |= ALTON;			// Left of Right Alt ON
						break;
		case 0x14:	wSysFlags |= CTRLON;			// Left Control ON
	}
	
	if (bCLed)		// LED states need changing ?
		KbLeds((BYTE)wSysFlags & 0x07);
}

/*---------------------------< HandleBreakCode >-----------------------------*/
void KbHandleBreakCode(BYTE bCode)
{

	// Break code so throw away next byte (unless Shift, Alt or Ctrl)
	switch (bCode)
	{
		case 0x12:
		case 0x59:	wSysFlags &= SHIFTOFF;
						break;
		case 0x11:	wSysFlags &= ALTOFF;
						break;
		case 0x14:	wSysFlags &= CTRLOFF;
	}
}

/*--------------------------< KbHandleExtCode >-----------------------------*/
BYTE KbHandleExtCode(BYTE bC)
{
	// Is it 0xF0 (break code) ?
	if (bC == 0x0f0) 
	{
		wSysFlags |= BREAKCODE;
		return FALSE;
	}
	
	switch (bC)
	{
		case 0x11:	bScanCode = 0x11; break;    	// Right Alt
		case 0x14:	bScanCode = 0x14; break;		// Right Ctrl
		case 0x69:	bScanCode = 0x85; break;		// End
		case 0x6b:	bScanCode = 0x88; break;   	// Left Arrow
		case 0x6c:	bScanCode = 0x8a; break;		// Home 
		case 0x70:  bScanCode = 0x84; break;		// Insert
		case 0x71:  bScanCode = 0x8d; break;		// Delete
		case 0x72:  bScanCode = 0x86; break;		// Down Arrow
		case 0x74:  bScanCode = 0x89; break;		// Right Arrow
		case 0x75:  bScanCode = 0x8b; break;		// Up Arrow
		case 0x7a:  bScanCode = 0x87; break;		// Page Down
		case 0x7d:  bScanCode = 0x8c; break;		// Page up
		default:    bScanCode = 0;	
	}
	return TRUE; 
}

/*----------------------------< GetRawCode >---------------------------------*/
BYTE GetRawCode(void)
{
	WORD wReg;
	
	while ((UART0FR) & 0x0010);
	wReg = UART0DR;

	//SprintHex(str, wReg, 2);
	//UartSend(str);
	//UartSend("h\r\n");

	return (BYTE)(wReg & 0xff);
}

/*----------------------------< RawCodeReceived >---------------------------*/
BYTE RawCodeReceived(void)
{
	if (UART0FR & 0x0010)
		return FALSE;
	else
		return TRUE;
}

/*-----------------------------< KbGetVersion >-----------------------------*/
WORD KbGetVersion(void)
{
	WORD wV;
	
	// Flush buffer
	while (KbCodeAvail())
		GetRawCode();
	
	// Send 'v'
	KbSendCode('v');
	SysDelay(10);
		
	// Should be 2 bytes
	if (KbCodeAvail())		
		wV = GetRawCode();
	else
		return 0;
	
	if (KbCodeAvail())		
		wV += ((WORD)GetRawCode() * 256); 
	else
		return 0;
		
	return wV;
}

/*----------------------------< KbCodeAvail >------------------------------*/
BYTE KbCodeAvail(void)
{	
	if (UART0FR & 0x0010)			// Look for character
		return FALSE;
	else
		return TRUE; 
}

/*----------------------------< KbSendCode >--------------------------------*/
void KbSendCode(BYTE bCode)
{
	while (UART0FR & 0x0020);
	UART0DR = bCode;
}

/*-----------------------------< SysDelay >---------------------------------*/
void SysDelay(WORD wDur)
{
	WORD wStart;
	
	wStart = RTCDR;							// get start time
	
	if ((wStart + wDur * 32) < wStart)	// overflow will happen ?
		while (RTCDR != (wStart+ wDur*32));		// wait here
	else
		while (RTCDR < (wStart+ wDur*32));		// wait here
}


// end of system.c
