/* GDTやIDTなどの、 descriptor table 関係 */

#include "bootpack.h"
#include "desctable.h"
#include "naskfunc.h"
#include "screen.h"
#include "debug.h"
#include "processman.h"

TSS32 tss_a;
TSS32 tss_b;
DWORD taska;
DWORD taskb;
char global_dt_buf[sizeof(GDT_IDT)];
GDT_IDT *global_dt = (GDT_IDT *)global_dt_buf;

void init_gdtidt(void)
{
	global_dt = new ((void *)global_dt_buf) GDT_IDT(NEW_GDT, NEW_IDT, LIMIT_GDT, LIMIT_IDT);

	DWORD *pagedir = (DWORD *)SYSTEM_PAGE_DIRECTORY_PHY;
	//debugprint("te2 %p\n", pagedir[SYSTEM_PAGE_FOR_PD >> 22]);
	DWORD *pagedirent = &pagedir[SYSTEM_PAGE_FOR_DT >> 22];

	*pagedirent = 0;//0xC0000000 -> 0x00000000 の4Mのページを作る
	*pagedirent |= ePAGE_DIR_ENT::PRESENT;
	*pagedirent |= ePAGE_DIR_ENT::READ_WRITE;
	*pagedirent |= ePAGE_DIR_ENT::PAGE_SIZE_4M;
	*pagedirent |= ePAGE_DIR_ENT::GLOBAL;
	*pagedirent |= ePAGE_DIR_ENT::ACCESS;
	*pagedirent |= ePAGE_DIR_ENT::DIRTY;

	tss_a.ldtr = 0;
	tss_a.iomap = 0x40000000;
	tss_a.cr3 = SYSTEM_PAGE_DIRECTORY_PHY;

	tss_b.ldtr = 0;
	tss_b.iomap = 0x40000000;
	tss_b.fs = tss_b.ds = tss_b.es = tss_b.gs = tss_b.ss = 1 << 3;
	tss_b.cs = 2 << 3;
	tss_b.esp = 0xf00000;
	tss_b.cr3 = SYSTEM_PAGE_DIRECTORY_PHY;
	tss_b.eflags = 0x00000202;
	//tss_b.eip = (DWORD)test;
	tss_b.eax = tss_b.ecx = tss_b.edx = tss_b.edi = tss_b.esi = tss_b.ebp = tss_b.ebx = 0;

	/* GDTの初期化 */
	for (int i = 0; i < global_dt->getgdtcount(); i++) {
		global_dt->set_segmdesc(i, 0, 0, 0);
	}
	int i = 1;
	global_dt->set_segmdesc(i++, 0xffffffff, 0x00000000, AR_DATA32_RW);
	global_dt->set_segmdesc(i++, 0xffffffff, 0x00000000, AR_CODE32_ER);
	global_dt->set_segmdesc(i++, 0xffffffff, 0x00000000, AR_DATA32_RW | AR_DPL_3);
	global_dt->set_segmdesc(i++, 0xffffffff, 0x00000000, AR_CODE32_ER | AR_DPL_3);

	taska = i;
	global_dt->set_segmdesc(i++, 103, (DWORD)&tss_a, AR_TSS32);
	taskb = i;
	global_dt->set_segmdesc(i++, 103, (DWORD)&tss_b, AR_TSS32);


	/* IDTの初期化 */
	for (int i = 0; i < global_dt->getidtcount(); i++) {
		global_dt->set_gatedesc(i, 0, 0, 0);
	}
	/* IDTの設定 */
	global_dt->set_gatedesc(0x00, (DWORD) asm_inthandler00, 2 * 8, AR_INTGATE32);
	global_dt->set_gatedesc(0x0a, (DWORD) asm_inthandler0a, 2 * 8, AR_INTGATE32);
	global_dt->set_gatedesc(0x0b, (DWORD) asm_inthandler0b, 2 * 8, AR_INTGATE32);
	global_dt->set_gatedesc(0x0c, (DWORD) asm_inthandler0c, 2 * 8, AR_INTGATE32);
	global_dt->set_gatedesc(0x0d, (DWORD) asm_inthandler0d, 2 * 8, AR_INTGATE32);
	global_dt->set_gatedesc(0x0e, (DWORD) asm_inthandler0e, 2 * 8, AR_INTGATE32);
	global_dt->set_gatedesc(0x20, (DWORD) asm_inthandler20, 2 * 8, AR_INTGATE32);
	//global_dt->set_gatedesc(0x21, (DWORD) asm_inthandler21, 2 * 8, AR_INTGATE32);
	//global_dt->set_gatedesc(0x2c, (DWORD) asm_inthandler2c, 2 * 8, AR_INTGATE32);
	//global_dt->set_gatedesc(0x40, (DWORD) asm_hrb_api,      2 * 8, AR_INTGATE32 + 0x60);
	global_dt->load();
	load_tr(taska << 3);


	return;
}

void GDT_IDT::init_app_gdtidt(TSS32 *tss, TSS32 *systss){
	/* GDTの初期化 */
	for (int i = 0; i <= LIMIT_GDT / 8; i++) {
		set_segmdesc(i, 0, 0, 0);
	}
	int i = 1;
	set_segmdesc(i++, 0xffffffff, 0x00000000, AR_DATA32_RW);
	set_segmdesc(i++, 0xffffffff, 0x00000000, AR_CODE32_ER);
	set_segmdesc(i++, 0xffffffff, 0x00000000, AR_DATA32_RW | AR_DPL_3);
	set_segmdesc(i++, 0xffffffff, 0x00000000, AR_CODE32_ER | AR_DPL_3);
	tasktr = i;
	set_segmdesc(i++, 103, (DWORD)tss, AR_TSS32 | AR_DPL_0);
	systr = i;
	set_segmdesc(i++, 103, (DWORD)systss, AR_TSS32 | AR_DPL_0);

	for (int i = 0; i <= LIMIT_IDT / 8; i++) {
		set_gatedesc(i, 0, 0, 0);
	}
	/*set_gatedesc(0x20, (DWORD) asm_inthandler20, 2 * 8, AR_INTGATE32);
	set_gatedesc(0x00, (DWORD) asm_inthandler00, 2 * 8, AR_INTGATE32);
	set_gatedesc(0x0a, (DWORD) asm_inthandler0a, 2 * 8, AR_INTGATE32);
	set_gatedesc(0x0b, (DWORD) asm_inthandler0b, 2 * 8, AR_INTGATE32);
	set_gatedesc(0x0c, (DWORD) asm_inthandler0c, 2 * 8, AR_INTGATE32);
	set_gatedesc(0x0d, (DWORD) asm_inthandler0d, 2 * 8, AR_INTGATE32);
	set_gatedesc(0x0e, (DWORD) asm_inthandler0e, 2 * 8, AR_INTGATE32);*/
}


GDT_IDT::GDT_IDT(DWORD gdtaddr, DWORD idtaddr, DWORD gdtlimit, DWORD idtlimit){
	init(gdtaddr, idtaddr, gdtlimit, idtlimit);
	ismine = false;
}

GDT_IDT::GDT_IDT(){
	ismine = false;
}

GDT_IDT::~GDT_IDT(){
}

void GDT_IDT::init(DWORD gdtaddr, DWORD idtaddr, DWORD gdtlimit, DWORD idtlimit){
	this->gdt = (SEGMENT_DESCRIPTOR *)gdtaddr;
	this->idt = (GATE_DESCRIPTOR    *)idtaddr;
	this->gdtlimit = gdtlimit;
	this->idtlimit = idtlimit;
}

void GDT_IDT::set_segmdesc(DWORD i, DWORD limit, DWORD base, DWORD ar)
{
	SEGMENT_DESCRIPTOR *sd = gdt + i;
	if (limit > 0xfffff) {
		ar |= 0x8000; /* G_bit = 1 */
		limit /= 0x1000;
	}
	sd->limit_low    = limit & 0xffff;
	sd->base_low     = base & 0xffff;
	sd->base_mid     = (base >> 16) & 0xff;
	sd->access_right = ar & 0xff;
	sd->limit_high   = ((limit >> 16) & 0x0f) | ((ar >> 8) & 0xf0);
	sd->base_high    = (base >> 24) & 0xff;
	return;
}

void GDT_IDT::set_gatedesc(DWORD i, DWORD offset, DWORD selector, DWORD ar)
{
	GATE_DESCRIPTOR *gd = idt + i;
	gd->offset_low   = offset & 0xffff;
	gd->selector     = selector;
	gd->dw_count     = (ar >> 8) & 0xff;
	gd->access_right = ar & 0xff;
	gd->offset_high  = (offset >> 16) & 0xffff;
	return;
}

void GDT_IDT::load(){
	load_gdtr(gdtlimit, (DWORD)gdt);
	load_idtr(idtlimit, (DWORD)idt);
}

DWORD GDT_IDT::getgdtcount()
{
	return (gdtlimit + 1) / sizeof(SEGMENT_DESCRIPTOR);
}

DWORD GDT_IDT::getidtcount()
{
	return (idtlimit + 1) / sizeof(GATE_DESCRIPTOR);
}


extern "C" void inthandler00(int *esp){
	debugprint("inthandler00 div error \n");
	for(;;){asm("hlt");}
}

extern "C" void inthandler21(int *esp){}
extern "C" void inthandler2c(int *esp){}
extern "C" void inthandler0a(int *esp){
	debugprint("inthandler0a invalid tss\n");
	for(;;){asm("hlt");}
}
extern "C" void inthandler0b(int *esp){
	debugprint("inthandler0b \n");
	for(;;){asm("hlt");}
}
extern "C" void inthandler0c(int *esp){
	debugprint("inthandler0c \n");

	for(;;){asm("hlt");}
}
extern "C" void inthandler0d(int *esp){
	debugprint("inthandler0d generic protection exception\n");

	for(;;){asm("hlt");}
}

extern "C" void inthandler0e(DWORD *esp){
	debugprint("inthandler0e  paging fault %p\n", *esp);
	for(;;){asm("hlt");}
	if(*esp & 1 == 0){
		//Pフラグがクリアされたセグメントへのアクセス
	}else{
		//フォルトの原因がページレベル保護違反であった。
	}
}
