//Keyboard.c
// Created: 04/17/03 Josh Holtrop
// Modified: 10/30/03
//for HOS

#define KBD_BUFFER_LENGTH 64

byte kbdFlags = 0;		//holds current keyboard flags - caps/num/scroll/shift/ctrl/alt
byte kbdAscii = 0;		//holds ASCII value of a key pressed
byte kbdScan = 0;		//holds the keyboard scan code of a key pressed

dword kbdBuffer[KBD_BUFFER_LENGTH];	//a buffer for all keypresses
int kbdBufferStart = 0;			//position of next key in buffer
int kbdBufferLen = 0;			//number of keys left in the buffer
byte kbdExt = 0;			//# of extended key codes left to input
byte kbdExt2 = 0;                       //# of 2nd-set-extended key codes left to input
byte ackReason = 0;		//used to record the reason why we would get an acknowledge byte (0xFA)


//these arrays convert a keyboard scan code to an ASCII character value
                                   //nul esc            bksp tab              lctl            lsft            rsft lalt caps F1  F2  F3  F4  F5  F6  F7  F8  F9  F10 numScrlNumPad-------                                  unknown---- F11 F12 unknown....
const byte SCAN2ASCII[128]      = "\000\0331234567890-=\010\011qwertyuiop[]\n\001asdfghjkl;'`\001\\zxcvbnm,./\001*\001 \001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001-\001\001\001+\001\001\001\001\001\002\002\002\001\001\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002";
const byte SCAN2ASCIISHIFT[128] = "\000\033!@#$%^&*()_+\010\011QWERTYUIOP{}\n\001ASDFGHJKL:\"~\001|ZXCVBNM<>?\001*\001 \001\001\001\001\001\001\001\001\001\001\001\001\001789-456+1230.\002\002\002\001\001\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002";

//====FUNCTIONS:
// The Keyboard Interrupt Service Routine
void isr_keyboard()
{
	kbdScan = inportb(0x60);
	//printf("\nKEYBOARD INTERRUPT: 0x%x", kbdScan);
	byte inState = inportb(0x61);
	outportb(0x61, inState|0x80);
	outportb(0x61, inState);
	
	//printf("IRQ 1: %x\n", kbdScan);

	if (kbdScan == 0xFA)	//250					//ACKnowledge
	{
		//printf("KBD_ACK 0x%x!\n", ackReason);
		if (ackReason == 0xED)					//reset LEDs
		{
			outportb(0x60, (kbdFlags & 0x07));
		}
		ackReason = 0;
	}
	if (kbdScan == 224)						//extended key
	{
		kbdExt = 1;
		eoi();
		return;
	}
	if (kbdScan == 225)						//2nd-set-extended key
	{
		kbdExt2 = 2;
		eoi();
		return;
	}

//====handle control keys::
	kbdAscii = 2;
	switch (kbdScan)						//control keys
	{
		case KBD_SCAN_LSHIFT:
			kbdFlags |= KBD_SHIFT;
			kbdAscii = 1;
			break;
		case KBD_SCAN_RSHIFT:
			kbdFlags |= KBD_SHIFT;
			kbdAscii = 1;
			break;
		case KBD_SCAN_LCTRL:
			kbdFlags |= KBD_CTRL;
			kbdAscii = 1;
			break;
		case KBD_SCAN_LALT:
			kbdFlags |= KBD_ALT;			
			kbdAscii = 1;
			break;

		case KBD_SCAN_LSHIFT + KBD_SCAN_RELEASED:
			kbdFlags &= (KBD_SHIFT ^ 0xFF);
			kbdAscii = 1;
			break;
		case KBD_SCAN_RSHIFT + KBD_SCAN_RELEASED:
			kbdFlags &= (KBD_SHIFT ^ 0xFF);
			kbdAscii = 1;
			break;
		case KBD_SCAN_LCTRL + KBD_SCAN_RELEASED:
			kbdFlags &= (KBD_CTRL ^ 0xFF);
			kbdAscii = 1;
			break;
		case KBD_SCAN_LALT + KBD_SCAN_RELEASED:
			kbdFlags &= (KBD_ALT ^ 0xFF);
			kbdAscii = 1;
			break;

		case KBD_SCAN_CAPS+KBD_SCAN_RELEASED:
			kbdFlags ^= KBD_CAPS;
			kbdAscii = 1;
			kbd_resetLEDs();				//update LEDs
			break;
		case KBD_SCAN_SCROLL+KBD_SCAN_RELEASED:
			kbdFlags ^= KBD_SCROLL;
			kbdAscii = 1;
			kbd_resetLEDs();				//update LEDs
			break;
		case KBD_SCAN_NUM+KBD_SCAN_RELEASED:
			kbdFlags ^= KBD_NUM;
			kbdAscii = 1;
			kbd_resetLEDs();				//update LEDs
			break;
	}
	if (kbdAscii == 1)
	{
		if (kbdExt > 0)
			kbdExt--;
		eoi();
		return;
	}
//====determine ASCII value of key::
	if (kbdExt > 0)						//extended key, kbdScan holds extended key
	{
		kbdExt--;
		kbdAscii = 1;
		switch (kbdScan)
		{
			case KBD_SCANE_ENTER:
				kbdAscii = '\n';	break;
			case 53:				// '/' character (divide on numpad)
				kbdAscii = '/';		break;
		}
	}
	else if (kbdExt2 > 0)					//extended key 2
	{
		kbdExt2--;
//		if (kbdScan == 69)				// (pause|break)
//			kbdAscii = 2;				//flag ascii value of 1 means control character (pausebreak)
//		else
			kbdAscii = 2;				//flag ascii value of 2 means ignore key (or unknown value)
	}
	else							//not an extended key
	{
			// if letter key
		if (((kbdScan >= 16) && (kbdScan <= 25)) || ((kbdScan >= 30) && (kbdScan <= 38)) || ((kbdScan >= 44) && (kbdScan <= 50)))
		{
			// if caps and shift are different (either one pressed, not both)
			if (((kbdFlags & KBD_SHIFT) != 0) & ((kbdFlags & KBD_CAPS) != 0))
				kbdAscii = SCAN2ASCII[kbdScan & 0x7F];
			else if (((kbdFlags & KBD_SHIFT) == 0) & ((kbdFlags & KBD_CAPS) == 0))
				kbdAscii = SCAN2ASCII[kbdScan & 0x7F];
			else
				kbdAscii = SCAN2ASCIISHIFT[kbdScan & 0x7F];
		}
			// if numpad key
		else if ((kbdScan >= 71) && (kbdScan <= 83))
		{
			// if numlock on
			if (kbdFlags & KBD_NUM)
				kbdAscii = SCAN2ASCIISHIFT[kbdScan & 0x7F];
			else
				kbdAscii = SCAN2ASCII[kbdScan & 0x7F];
		}
			// other key
		else
		{
			if ((kbdFlags & KBD_SHIFT) != 0)
				kbdAscii = SCAN2ASCIISHIFT[kbdScan & 0x7F];
			else
				kbdAscii = SCAN2ASCII[kbdScan & 0x7F];
		}
	}

//====do something with key::
//	printf("kbdScan == %d\nkbdAscii == %d\nkbdFlags == %d\n", kbdScan, kbdAscii, kbdFlags);
	if ((kbdScan == 83) && (kbdFlags & KBD_CTRL) && (kbdFlags & KBD_ALT))
	{
		printf("Initiating reboot.");
		restart();
	}
	if (kbdAscii == 2)							//unknown key / ignore key
	{
		eoi();
		return;
	}
	if (kbdScan < KBD_SCAN_RELEASED)					//a key was pressed, save it
	{
		if (kbdBufferLen >= KBD_BUFFER_LENGTH)				//no key slots available
		{
			eoi();
			return;
		}
		else
		{
			kbdBuffer[(kbdBufferStart+kbdBufferLen++)%KBD_BUFFER_LENGTH] = (dword) ((kbdFlags << 16) | (kbdScan << 8) | kbdAscii);
//			printf("S:%d\tL:%d\tR:%x\n", kbdBufferStart, kbdBufferLen, kbdBuffer[kbdBufferStart]);
		}
	}

	eoi();

}

//Switches the case of an ASCII character
/*inline byte (byte asciiCode)
{
	if ((asciiCode >= 'A') & (asciiCode <= 'Z'))
		return (asciiCode + ('a' - 'A'));
	if ((asciiCode >= 'a') & (asciiCode <= 'z'))
		return (asciiCode - ('a' - 'A'));
	return asciiCode;
}*/

//Gets a key from the buffer, returns 0 if no keys available, returns immediately
dword kbdGetKey()
{
	if (kbdBufferLen == 0)					//buffer empty
		return 0;
	dword retVal = kbdBuffer[kbdBufferStart];
	kbdBufferStart++;
	kbdBufferLen--;
	if (kbdBufferStart >= KBD_BUFFER_LENGTH)
		kbdBufferStart = 0;
	return retVal;
}

//Gets a key from the buffer, if no keys available, waits for one to be entered
dword kbdWaitKey()
{
	for (;;)
	{
		if (kbdBufferLen != 0)					//buffer empty
			break;
	}		
	dword retVal = kbdBuffer[kbdBufferStart];
	kbdBufferStart++;
	kbdBufferLen--;
	if (kbdBufferStart >= KBD_BUFFER_LENGTH)
		kbdBufferStart = 0;
	return retVal;
}

//Resets the keyboard LEDs to reflect the current state of the num lock, caps lock, and scroll lock bits
inline void kbd_resetLEDs()
{
	outportb(0x60, 0xED);
	//outportb(0x60, (kbdFlags & 0x07));
	ackReason = 0xED;
}






