/*******************************************************************************/
/* COS2000 - Compatible Operating System - LGPL v3 - Hordé Nicolas             */
/*                                                                             */
#include "interrupts.h"
#include "types.h"
#include "asm.h"
#include "memory.h"
#include "video.h"
#include "gdt.h"
#include "system.h"
#include "debug.h"
#include "process.h"

/******************************************************************************/
/* Déclenché lors de l'appel d'une interruption */

__attribute__ ((noreturn)) void interruption_handler(regs *dump)
{
	u32 interruption=dump->eip;
	exception_stack_noerror *caller = (exception_stack_noerror*) ((u32*)dump->esp+1);
	bool noerror,user;
	if ((caller->cs & 0xFFF8)==SEL_KERNEL_CODE || (caller->cs & 0xFFF8)==SEL_USER_CODE)
	{
		noerror=true;
		dump->eip = caller->eip;
		dump->cs = caller->cs;
		dump->eflags = caller->eflags;
		if ((dump->cs & 0xFFF8)==SEL_KERNEL_CODE)
		{
			dump->esp = (u32) caller + sizeof(exception_stack_noerror);
			user=false;
		}
		else
		{
			dump->esp = (u32) ((exception_stack_noerror_user*) caller)->esp;
			dump->ss = (u32) ((exception_stack_noerror_user*) caller)->ss;
			user=true;
		}
	}
	else
	{
		noerror=false;
		dump->eip = ((exception_stack*)caller)->eip;
		dump->cs = ((exception_stack*)caller)->cs;
		if ((dump->cs & 0xFFF8)==SEL_KERNEL_CODE)
		{
			dump->esp = (u32) caller + sizeof(exception_stack);
			user=false;
		}
		else
		{
			dump->esp = (u32) ((exception_stack_user*) caller)->esp;
			dump->ss = (u32) ((exception_stack_user*) caller)->ss;
			user=true;
		}
	}
	switch (interruption)
	{
		case 20:
			show_lightcpu(dump);
			break;
		default:
			print("Appel d'une interruption\r\n");
	}
		if ((dump->cs & 0xFFF8)==SEL_KERNEL_CODE)
		{
			setESP(dump);
			restcpu_kernel();
		}
		else
		{
			setESP(dump);
			restcpu_user();
			iret();
		}
}

/******************************************************************************/
/* Les expections */

static u8 ex14_errors1[] =
	"Supervisory process tried to read a non-present page entry";
static u8 ex14_errors2[] =
	"Supervisory process tried to read a page and caused a protection fault";
static u8 ex14_errors3[] =
	"Supervisory process tried to write to a non-present page entry";
static u8 ex14_errors4[] =
	"Supervisory process tried to write a page and caused a protection fault";
static u8 ex14_errors5[] =
	"User process tried to read a non-present page entry";
static u8 ex14_errors6[] =
	"User process tried to read a page and caused a protection fault";
static u8 ex14_errors7[] =
	"User process tried to write to a non-present page entry";
static u8 ex14_errors8[] =
	"User process tried to write a page and caused a protection fault";
static u8 *ex14_errors[] =
	{ &ex14_errors1, &ex14_errors2, &ex14_errors3, &ex14_errors4,
	&ex14_errors5, &ex14_errors6, &ex14_errors7, &ex14_errors8
};

__attribute__ ((noreturn)) void exception_handler(regs *dump)
{
	u32 exception=dump->eip;
	exception_stack_noerror *caller = (exception_stack_noerror*) ((u32*)dump->esp+1);
	bool noerror,user;
	if ((caller->cs & 0xFFF8)==SEL_KERNEL_CODE || (caller->cs & 0xFFF8)==SEL_USER_CODE)
	{
		noerror=true;
		dump->eip = caller->eip;
		dump->cs = caller->cs;
		dump->eflags = caller->eflags;
		if ((dump->cs & 0xFFF8)==SEL_KERNEL_CODE)
		{
			dump->esp = (u32) caller + sizeof(exception_stack_noerror);
			user=false;
		}
		else
		{
			dump->esp = (u32) ((exception_stack_noerror_user*) caller)->esp;
			dump->ss = (u32) ((exception_stack_noerror_user*) caller)->ss;
			user=true;
		}
	}
	else
	{
		noerror=false;
		dump->eip = ((exception_stack*)caller)->eip;
		dump->cs = ((exception_stack*)caller)->cs;
		if ((dump->cs & 0xFFF8)==SEL_KERNEL_CODE)
		{
			dump->esp = (u32) caller + sizeof(exception_stack);
			user=false;
		}
		else
		{
			dump->esp = (u32) ((exception_stack_user*) caller)->esp;
			dump->ss = (u32) ((exception_stack_user*) caller)->ss;
			user=true;
		}
	}
	switch (exception)
	{
		case 0:
			cpuerror("#DE Divide error", dump, false);
		case 1:
			changevc(6);
			clearscreen();
			show_lightcpu(dump);
			printf("\r\n\033[7m[P]\033[0m PAS A PAS \033[7m D \033[0m PAS A PAS DETAILLE \033[7m C \033[0m CONTINUER \033[7m S \033[0m STOPPER \033[7m V \033[0m VOIR \033[7m S \033[0m SCINDER");
			sti();
			u8      ascii = waitascii();
			cli();
			if (ascii == 'P' || ascii == 'p')
				setdebugreg(0,
					    caller->eip + disasm(caller->eip, NULL, false),
					    DBG_EXEC);
			else if (ascii == 'D' || ascii == 'd')
				setdebugreg(0, 0, DBG_CLEAR);
			else if (ascii == 'C' || ascii == 'c')
				setdebugreg(0, 0, DBG_CLEAR);
			else if (ascii == 'S' || ascii == 's')
			{
				changevc(0);
				sti();
				initselectors(getinitretry());
			}
			changevc(0);
			goto endofexception;
		case 2:
			cpuerror("NMI Non-maskable hardware interrupt", dump, false);
		case 3:
			cpuerror("#BP INT3 instruction", dump, true);
			iret();
		case 4:
			cpuerror("#OF INTO instruction detected overflow", dump, false);
		case 5:
			cpuerror("#BR BOUND instruction detected overrange", dump, false);
		case 6:
			cpuerror("#UD Invalid instruction opcode", dump, false);
		case 7:
			cpuerror("#NM No coprocessor", dump, false);
		case 8:
			cpuerror("#DF Double fault", dump, false);
		case 9:
			cpuerror("Coprocessor segment overrun", dump, false);	
		case 10:
			cpuerror("#TS Invalid task state segment (TSS)", dump, false);
		case 11:
			cpuerror("#NP Segment not present", dump, false);
		case 12:
			cpuerror("#SS Stack fault", dump, false);
		case 13:
			cpuerror("#GP General protection fault (GPF)", dump, false);
		case 14:
			if (dump->cr2 >= USER_CODE && dump->cr2 < USER_STACK)
			{
				virtual_range_new(findcurrentprocess()->pdd,
						  (u8 *) (dump->cr2 & 0xFFFFF000),
						  PAGESIZE, PAGE_ALL);
			}
			else
			{
				printf("Page fault - %s at adress %Y cs:eip - %Y:%Y\r\n",
					 ex14_errors[((exception_stack*) caller)->error_code & 0xF], dump->cr2,
					 dump->cs, dump->eip);
				cpuerror("#PGF Page fault", dump, false);
			}
			goto endofexception;
		case 15:
			cpuerror("(reserved)", dump, false);
		case 16:
			cpuerror("#MF Coprocessor error", dump, false);
		case 17:
			cpuerror("#AC Alignment check", dump, false);
		case 18: 
			cpuerror("#MC Machine check", dump, false);
	}
	endofexception:
		if ((dump->cs & 0xFFF8)==SEL_KERNEL_CODE)
		{
			setESP(dump);
			restcpu_kernel();
		}
		else
		{
			setESP(dump);
			restcpu_user();
			iret();
		}
}

/******************************************************************************/
/* Les IRQ par défaut */

__attribute__((interrupt)) void irq0(exception_stack_noerror *caller)
{	
	print("irq 0");
	irqendmaster();
}

__attribute__((interrupt)) void irq1(exception_stack_noerror *caller)
{
	print("irq 1");
	while ((inb(0x64) & 1) == 0);
	inb(0x60);
	irqendmaster();
}

__attribute__((interrupt)) void irq2(exception_stack_noerror *caller)
{	print("irq 2");
	irqendmaster();
}

__attribute__((interrupt)) void irq3(exception_stack_noerror *caller)
{
	print("irq 3");
	irqendmaster();
}

__attribute__((interrupt)) void irq4(exception_stack_noerror *caller)
{
	print("irq 4");
	irqendmaster();
}

__attribute__((interrupt)) void irq5(exception_stack_noerror *caller)
{
	print("irq 5");
	irqendmaster();
}

__attribute__((interrupt)) void irq6(exception_stack_noerror *caller)
{
	print("irq 6");
	irqendmaster();
}

__attribute__((interrupt)) void irq7(exception_stack_noerror *caller)
{
	print("irq 7");
	irqendmaster();
}

__attribute__((interrupt)) void irq8(exception_stack_noerror *caller)
{
	print("irq 8");
	irqendslave();
	irqendmaster();
}

__attribute__((interrupt)) void irq9(exception_stack_noerror *caller)
{
	print("irq 9");
	irqendslave();
	irqendmaster();
}

__attribute__((interrupt)) void irq10(exception_stack_noerror *caller)
{
	print("irq 10");
	irqendslave();
	irqendmaster();
}

__attribute__((interrupt)) void irq11(exception_stack_noerror *caller)
{
	print("irq 11");
	irqendslave();
	irqendmaster();
}

__attribute__((interrupt)) void irq12(exception_stack_noerror *caller)
{
	print("irq 12");
	while ((inb(0x64) & 1) == 0);
	inb(0x60);
	irqendslave();
	irqendmaster();
}

__attribute__((interrupt)) void irq13(exception_stack_noerror *caller)
{
	print("irq 13");
	irqendslave();
	irqendmaster();
}

__attribute__((interrupt)) void irq14(exception_stack_noerror *caller)
{
	print("irq 14");
	irqendslave();
	irqendmaster();
}

__attribute__((interrupt)) void irq15(exception_stack_noerror *caller)
{
	print("irq 15");
	irqendslave();
	irqendmaster();
}
