/*******************************************************************************/
/* COS2000 - Compatible Operating System - LGPL v3 - Hordé Nicolas             */
/*                                                                             */
#include "interrupts.h"
#include "types.h"
#include "asm.h"
#include "memory.h"
#include "keyboard.h"
#include "vga.h"
#include "video.h"

static u8 bufferscan[256] = { 0 };
static u8 bufferascii[256] = { 0 };

static u8 ptrscan = 0;
static u8 ptrascii = 0;
static u16 kbdstatus, breakcode;

/******************************************************************************/
/* Tables clavier */

static const u8 set1_normal[] = {
	0, 0x1B, '&', 'é', '\"', '\'', '(', '-',
	'è', '_', 'ç', 'à', ')', '=', '\b', '\t',
	'a', 'z', 'e', 'r', 't', 'y', 'u', 'i',
	'o', 'p', '^', '$', '\r', 0, 'q', 's',
	'd', 'f', 'g', 'h', 'j', 'k', 'l', 'm',
	'ù', '²', 0, '*', 'w', 'x', 'c', 'v',
	'b', 'n', ',', ';', ':', '!', 0, '*',
	0, ' ', 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, '7',
	'8', '9', '-', '4', '5', '6', '+', '1',
	'2', '3', '0', '.', 0, 0, '<', 0,
	0
};

static const u8 set1_shift[] = {
	0, 0x1B, '1', '2', '3', '4', '5', '6',
	'7', '8', '9', '0', '°', '+', '\b', '\t',
	'A', 'Z', 'E', 'R', 'T', 'Y', 'U', 'I',
	'O', 'P', '¨', '£', '\r', 0, 'Q', 'S',
	'D', 'F', 'G', 'H', 'J', 'K', 'L', 'M',
	'%', 0, 0, 'µ', 'W', 'X', 'C', 'V',
	'B', 'N', '?', '.', '/', '§', 0, '*',
	0, ' ', 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, '7',
	'8', '9', '-', '4', '5', '6', '+', '1',
	'2', '3', '0', '.', 0, 0, '>', 0,
	0
};

static const u8 set1_alt[] = {
	0, 0x1B, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, '\r', 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, '*',
	0, ' ', 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, '7',
	'8', '9', '-', '4', '5', '6', '+', '1',
	'2', '3', '0', '.', 0, 0, 0, 0,
	0
};

static const u8 set1_altgr[] = {
	0, 0x1B, 0, '~', '#', '{', '[', '|',
	'`', '\\', '^', '@', ']', '}', '\b', '\t',
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, '¤', '\r', 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, '*',
	0, ' ', 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, '7',
	'8', '9', '-', '4', '5', '6', '+', '1',
	'2', '3', '0', '.', 0, 0, 0, 0,
	0
};

static const u8 set1_ctrl[] = {
	0, 0x1B, 0, 0, 0, 0, 0x1B, 0,
	0, 0x1C, 0, 0, 0x1D, 0, 0, 0x1F,
	0x01, 0x1A, 0x05, 0x012, 0x14, 0x19, 0x15, 0x09,
	0x0F, 0x10, 0x1E, 0, 0, 0, 0x11, 0x13,
	0x04, 0x06, 0x07, 0x08, 0x0A, 0x0B, 0x0C, 0x0D,
	0, 0, 0, 0, 0x17, 0x18, 0x03, 0x16,
	0x02, 0x0E, 0, 0, 0, 0, 0, '*',
	0, ' ', 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, '7',
	'8', '9', '-', '4', '5', '6', '+', '1',
	'2', '3', '0', '.', 0, 0, 0, 0,
	0
};

/******************************************************************************/

/* Attend une chaine de caractère de taille max */

u8     *getstring(u8 * temp)
{
	u8      maxwidth = strlen(temp);
	u8     *pointer = temp;
	u8      ascii = 0;
	while (ascii != '\r')
	{
		ascii = waitascii();
		if (ascii == '\b' && pointer > temp)
		{
			pointer--;
			putchar(ascii);
		}
		else if (ascii > 31 && pointer <= temp + 80)
		{
			*pointer++ = ascii;
			putchar(ascii);
		}
	}
	*pointer = '\000';
	return temp;
}

/******************************************************************************/
/* Fonction qui attend l'appuie d'une touche générant un code ASCII puis le retourne */
/* SYSCALL 
{
"ID":1,
"LIBRARY":"libsys",
"NAME":"waitkey",
"INTERNALNAME":"waitascii",
"DESCRIPTION":"Wait for user to press a key and return the ascii code pressed",
"ARGS": [],
"RETURN":"u8"
}
END-SYSCALL */

u8 waitascii(void)
{
	u8      oldptrascii = ptrascii;
	while ((oldptrascii == ptrascii));
	return bufferascii[ptrascii];
}

/******************************************************************************/
/* Envoi d'une commande vers le contrôleur de clavier */

void outkbd(u8 port, u8 data)
{
	u32     timeout;
	u8      state;

/* timeout */
	for (timeout = 500000L; timeout != 0; timeout--)
	{
		state = inb(0x64);
/* vide le buffer du 8042 */
		if ((state & 0x02) == 0)
			break;
	}
	if (timeout != 0)
		outb(port, data);
}

/******************************************************************************/
/* Redemarre l'ordinateur */

void reboot(void)
{
	u8      temp;
	cli();
/* vide le 8042 */
	do
	{
		temp = inb(0x64);
		if ((temp & 0x01) != 0)
		{
			(void) inb(0x60);
			continue;
		}
	}
	while ((temp & 0x02) != 0);
/* active le reset CPU */
	outb(0x64, 0xFE);
	while (1)
		/* boucle infinie */ ;
}

/******************************************************************************/
/* Converti un scancode vers une code ASCII */

unsigned convert(u32 keypressed)
{
	u8      temp, key, lastscan;
/* garde le dernier pointeur du buffer scan */
	lastscan = ptrscan;
/* incrémente le pointeur est assigne au buffer le dernier scancode */
	ptrscan++;
	if (ptrscan == 255)
		ptrscan == 0;
	bufferscan[ptrscan] = keypressed;
/* break key (touche relaché) ? */
	if (keypressed >= 0x80)
		breakcode = 1;
	key = (keypressed & 0x7F);
/* Mise a jour des flags lors du relachement de touches de controle */
	if (breakcode)
	{
		if (key == SCAN_ALT)
		{
			kbdstatus &= ~STATUS_ALT;
			/* si ALT GR (E01D) alors activer aussi control */
			if (bufferscan[lastscan] == 0xE0)
				kbdstatus &= ~STATUS_CTRL;
		}
		else if (key == SCAN_CTRL)
			kbdstatus &= ~STATUS_CTRL;
		else if (key == SCAN_LEFTSHIFT || key == SCAN_RIGHTSHIFT)
			kbdstatus &= ~STATUS_SHIFT;
		breakcode = 0;
		return 0;
	}
/* Mise a jour des flags lors de l'appuie de touches de controle */
	if (key == SCAN_ALT)
	{
		kbdstatus |= STATUS_ALT;
		/* si ALT GR (E01D) alors desactiver aussi control */
		if (bufferscan[lastscan] == 0xE0)
			kbdstatus |= STATUS_CTRL;
		return 0;
	}
	else if (key == SCAN_CTRL)
	{
		kbdstatus |= STATUS_CTRL;
		return 0;
	}
	else if (key == SCAN_LEFTSHIFT || key == SCAN_RIGHTSHIFT)
	{
		kbdstatus |= STATUS_SHIFT;
		return 0;
	}

	else if ((key >= SCAN_F1) && (key <= SCAN_F8))
	{
		changevc(key - SCAN_F1);
	}

	else if (key == SCAN_F9)
	{
		int20;
	}

	else if (key == SCAN_F10)
	{
		apply_nextvideomode();
	}

/* Scroll Lock, Num Lock, and Caps Lock mise a jour des leds */
	else if (key == SCAN_SCROLLLOCK)
	{
		kbdstatus ^= STATUS_SCRL;
		goto LEDS;
	}
	else if (key == SCAN_NUMLOCK)
	{
		kbdstatus ^= STATUS_NUM;
		goto LEDS;
	}
	else if (key == SCAN_CAPSLOCK)
	{
		kbdstatus ^= STATUS_CAPS;
	      LEDS:
		outkbd(0x60, 0xED);	/* "mise a jour des LEDS */
		temp = 0;
		if (kbdstatus & STATUS_SCRL)
			temp |= 1;
		if (kbdstatus & STATUS_NUM)
			temp |= 2;
		if (kbdstatus & STATUS_CAPS)
			temp |= 4;
		outkbd(0x60, temp);	/* 3 bits de poids faible pour les LEDs */
		return 0;
	}
/* Appuie de CRTL + ALT + SUPR ? */
	if ((kbdstatus & STATUS_CTRL) && (kbdstatus & STATUS_ALT)
	    && (key == 73))
	{
		print("redemarrage du systeme");
		reboot();
	}
/* est ce un code etendu */
	if ((bufferscan[lastscan] == 0xE0)
	    || ((kbdstatus & STATUS_NUM) && (key >= 0x47) && (key <= 0x53)
		&& (key != 0x4A) && (key != 0x4e)))
		/* exceptions */
	{
		/* '/' (E035) */
		if (key == 0x35)
			return '/';
		/* '\r' (E01C) 2ème enter numérique */
		if (key == 0x1C)
			return '\r';
		/* 0x11 (E048) device control 1) */
		if (key == 0x48)
			return 0x11;
		/* 0x12 (E050) device control 2) */
		if (key == 0x50)
			return 0x12;
		/* 0x13 (E04b) device control 3) */
		if (key == 0x4b)
			return 0x13;
		/* 0x14 (E04d) device control 4) */
		if (key == 0x4d)
			return 0x14;
		/* 0x02 (E049) start of text) */
		if (key == 0x49)
			return 0x2;
		/* 0x03 (E051) end of text) */
		if (key == 0x51)
			return 0x3;
		/* 0x10 (E047) Line feed) */
		if (key == 0x47)
			return '\n';
		/* 0x1A (E052) Substitution) */
		if (key == 0x52)
			return 0x1A;
		/* 0x18 (E053) Cancel) */
		if (key == 0x53)
			return 0x18;
		/* 0x19 (E04f) End of medium) */
		if (key == 0x4f)
			return 0x19;
		return 0x00;
	}
	else
	{
/* detecte les SCANCODES invalides */
		if (key >= sizeof(set1_normal) / sizeof(set1_normal[0]))
			return 0;
/* converti le scancode en code ASCII en fonction du statut*/
		if (kbdstatus & STATUS_SHIFT || kbdstatus & STATUS_CAPS)
			temp = set1_shift[key];
		else if ((kbdstatus & STATUS_ALT)
			 && (kbdstatus & STATUS_CTRL))
			temp = set1_altgr[key];
		else if (kbdstatus & STATUS_CTRL)
			temp = set1_ctrl[key];
		else if (kbdstatus & STATUS_ALT)
			temp = set1_alt[key];
		else
			temp = set1_normal[key];
	}
/* si scancode non reconnu fin de fonction */
	if (temp == 0)
		return temp;
/* Renvoie le Code ascii */
	return temp;
}

/******************************************************************************/
/* Handler d'interruption IRQ 1 pour le clavier */

__attribute__((interrupt)) void keyboard_handler(exception_stack_noerror *caller)
{
	cli();
	u8      scancode, ascii;
	while ((inb(0x64) & 1) == 0);
	scancode = inb(0x60);
	ascii = convert(scancode);
	if (ascii != 0)
	{
		ptrascii++;
		if (ptrascii == 255)
			ptrascii == 0;
		bufferascii[ptrascii] = ascii;
	}
	irqendmaster();
	sti();
}

/******************************************************************************/
