// EE initialization functions
// [made by] [RO]man, zerofrog
#include <tamtypes.h>
#include <stdio.h>

#include "eekernel.h"
#include "eeirq.h"

void InitializeGS();
void InitializeGIF();
void InitializeDMAC(int code);
void InitializeVU1();
void InitializeVIF1();
void InitializeIPU();
void InitializeVIF0();
void InitializeVU0();
void InitializeFPU();
void InitializeScratchPad();
void InitializeUserMemory(u32 base);
void InitializeINTC(int a);
void InitializeTIMER();

////////////////////////////////////////////////////////////////////
//8000AA60
////////////////////////////////////////////////////////////////////
void InitializeGS()
{
}

////////////////////////////////////////////////////////////////////
//8000AB98
////////////////////////////////////////////////////////////////////
void InitializeGIF()
{
	GIF_CTRL = 1;
	__asm__ ("sync\n");
	GIF_FIFO = 0;
}

////////////////////////////////////////////////////////////////////
//8000AD68		SYSCALL 001 ResetEE
////////////////////////////////////////////////////////////////////
int  _ResetEE(int init)
{
	if (init & 0x01) { __printf("# Initialize DMAC ...\n"); InitializeDMAC(0x31F); }
	if (init & 0x02) { __printf("# Initialize VU1 ...\n");  InitializeVU1(); }
	if (init & 0x04) { __printf("# Initialize VIF1 ...\n"); InitializeVIF1(); }
	if (init & 0x08) { __printf("# Initialize GIF ...\n");  InitializeGIF(); }
	if (init & 0x10) { __printf("# Initialize VU0 ...\n");  InitializeVU0(); }
	if (init & 0x04) { __printf("# Initialize VIF0 ...\n"); InitializeVIF0(); }
	if (init & 0x40) { __printf("# Initialize IPU ...\n");  InitializeIPU(); }

	InitializeINTC(0xC);
//	return (*(int*)0x1000F410 &= 0xFFFBFFFF); code never reached :)
}

////////////////////////////////////////////////////////////////////
//8000AE88
////////////////////////////////////////////////////////////////////
int Initialize()
{
	__printf("# Initialize Start.\n");
	__printf("# Initialize GS ...");

	InitializeGS();
	_SetGsCrt(1, 2, 1);
	__printf("\n");

	__printf("# Initialize INTC ...\n");
	InitializeINTC(0xFFFF);

	__printf("# Initialize TIMER ...\n");
	InitializeTIMER();

	ResetEE(0x7F);

	__printf("# Initialize FPU ...\n");
	InitializeFPU();

	__printf("# Initialize User Memory ...\n");
	InitializeUserMemory(0x80000);

	__printf("# Initialize Scratch Pad ...\n");
	InitializeScratchPad();

	__printf("# Initialize Done.\n");
}

////////////////////////////////////////////////////////////////////
//8000AF50
////////////////////////////////////////////////////////////////////
int  Restart()
{
	__printf("# Restart.\n");
	__printf("# Initialize GS ...");

	INTC_STAT = 4;
	while (INTC_STAT & 4) { __asm__ ("nop\nnop\nnop\n"); }
	INTC_STAT = 4;

	InitializeGS();
	_SetGsCrt(1, 2, 1);
	__printf("\n");

	__printf("# Initialize INTC ...\n");
	InitializeINTC(0xDFFD);

	__printf("# Initialize TIMER ...\n");
	InitializeTIMER();

	ResetEE(0x7F);

	__printf("# Initialize FPU ...\n");
	InitializeFPU();

	__printf("# Initialize User Memory ...\n");
	InitializeUserMemory(0x82000);

	__printf("# Initialize Scratch Pad ...\n");
	InitializeScratchPad();

	__printf("# Restart Done.\n");

	//wait for syncing IOP
	while (SBUS_SMFLG & SBFLG_IOPSYNC) { __asm__ ("nop\nnop\nnop\n"); }

	SBUS_SMFLG=SBFLG_IOPSYNC;
}

////////////////////////////////////////////////////////////////////
//8000B0A0
////////////////////////////////////////////////////////////////////
void InitializeDMAC(int code)
{
	int i;
	int *addr;

	for (i=0; i<10; i++) {
		if (!(code & (1<<i))) continue;

		addr = (int*)dmac_CHCR[i];
		addr[0x80/4] = 0;
		addr[0x00/4] = 0;
		addr[0x30/4] = 0;
		addr[0x10/4] = 0;
		addr[0x50/4] = 0;
		addr[0x40/4] = 0;
	}

	DMAC_STAT = (code & 0xFFFF) | 0xE000;
	DMAC_STAT = (DMAC_STAT & 0xFFFF0000) & ((code | 0x6000) << 16);

	if ((code & 0x7F) == 0x7F) {
		DMAC_CTRL = 0;
		DMAC_PCR  = 0;
	} else {
		DMAC_CTRL = 0;
		DMAC_PCR  = DMAC_PCR & (((~code) << 16) | (~code));
	}

	DMAC_SQWC = 0;
	DMAC_RBSR = 0;
	DMAC_RBOR = 0;
}

////////////////////////////////////////////////////////////////////
//8000B1F0
////////////////////////////////////////////////////////////////////
void InitializeVU1()
{
}

////////////////////////////////////////////////////////////////////
//8000B2D8
////////////////////////////////////////////////////////////////////
void InitializeVIF1()
{
}

////////////////////////////////////////////////////////////////////
//8000B3B0
////////////////////////////////////////////////////////////////////
void InitializeIPU()
{
}

////////////////////////////////////////////////////////////////////
//8000B6F8
////////////////////////////////////////////////////////////////////
void InitializeVIF0()
{
}

////////////////////////////////////////////////////////////////////
//8000B778
////////////////////////////////////////////////////////////////////
void InitializeVU0()
{
}

////////////////////////////////////////////////////////////////////
//8000B7A8
////////////////////////////////////////////////////////////////////
void InitializeFPU()
{
	__asm__ (
		"mtc1    $0, $0\n"
		"mtc1    $0, $1\n"
		"mtc1    $0, $2\n"
		"mtc1    $0, $3\n"
		"mtc1    $0, $4\n"
		"mtc1    $0, $5\n"
		"mtc1    $0, $6\n"
		"mtc1    $0, $7\n"
		"mtc1    $0, $8\n"
		"mtc1    $0, $9\n"
		"mtc1    $0, $10\n"
		"mtc1    $0, $11\n"
		"mtc1    $0, $12\n"
		"mtc1    $0, $13\n"
		"mtc1    $0, $14\n"
		"mtc1    $0, $15\n"
		"mtc1    $0, $16\n"
		"mtc1    $0, $17\n"
		"mtc1    $0, $18\n"
		"mtc1    $0, $19\n"
		"mtc1    $0, $20\n"
		"mtc1    $0, $21\n"
		"mtc1    $0, $22\n"
		"mtc1    $0, $23\n"
		"mtc1    $0, $24\n"
		"mtc1    $0, $25\n"
		"mtc1    $0, $26\n"
		"mtc1    $0, $27\n"
		"mtc1    $0, $28\n"
		"mtc1    $0, $29\n"
		"mtc1    $0, $30\n"
		"mtc1    $0, $31\n"
		"adda.s  $f0, $f1\n"
		"sync\n"
		"ctc1    $0, $31\n"
	);
}

////////////////////////////////////////////////////////////////////
//8000B840
////////////////////////////////////////////////////////////////////
void InitializeScratchPad()
{
	u128 *p;

	for (p=(u128*)0x70000000; (u32)p<0x70004000; p++) *p=0;
}

////////////////////////////////////////////////////////////////////
//8000B878
////////////////////////////////////////////////////////////////////
void InitializeUserMemory(u32 base)
{
	u128 *memsz;
	u128 *p = (u128*)base;
	
	for (memsz=(u128*)_GetMemorySize(); p<memsz; p++)
        *p = 0;
}

////////////////////////////////////////////////////////////////////
//8000B8D0
////////////////////////////////////////////////////////////////////
void InitializeINTC(int a)
{
	a &= 0xDFFD;
	INTC_STAT &= a;
	INTC_MASK &= a;
}

////////////////////////////////////////////////////////////////////
//8000B900
////////////////////////////////////////////////////////////////////
void InitializeTIMER()
{
	RCNT0_MODE   = 0xC00;
	RCNT0_COUNT  = 0;
	RCNT0_TARGET = 0xFFFF;
	RCNT0_HOLD   = 0;

	RCNT1_MODE   = 0xC00;
	RCNT1_COUNT  = 0;
	RCNT1_TARGET = 0xFFFF;
	RCNT1_HOLD   = 0;

	RCNT2_MODE   = 0xC80;
	RCNT2_COUNT  = 0;
	RCNT2_TARGET = 0xFFFF;

	RCNT3_MODE   = 0xC83;
	RCNT3_COUNT  = 0;
	RCNT3_TARGET = 0xFFFF;

	InitializeINTC(0x1E00);
	_InitRCNT3();
}

u32 tlb1_data[] = {
	0x00000000, 0x70000000, 0x80000007, 0x00000007,
	0x00006000, 0xFFFF8000, 0x00001E1F, 0x00001F1F,
	0x00000000, 0x10000000, 0x00400017, 0x00400053,
	0x00000000, 0x10002000, 0x00400097, 0x004000D7, 
	0x00000000, 0x10004000,	0x00400117,	0x00400157, 
	0x00000000, 0x10006000, 0x00400197, 0x004001D7,
	0x00000000, 0x10008000, 0x00400217, 0x00400257, 
	0x00000000, 0x1000A000,	0x00400297,	0x004002D7, 
	0x00000000, 0x1000C000, 0x00400313, 0x00400357,
	0x00000000, 0x1000E000, 0x00400397, 0x004003D7,
	0x0001E000, 0x11000000,	0x00440017,	0x00440415, 
	0x0001E000, 0x12000000, 0x00480017, 0x00480415,
	0x01FFE000, 0x1E000000, 0x00780017, 0x007C0017,
};

u32 tlb2_data[] = {
	0x0007E000, 0x00080000, 0x0000201F, 0x0000301F,
	0x0007E000, 0x00100000, 0x0000401F, 0x0000501F, 
	0x0007E000, 0x00180000, 0x0000601F, 0x0000701F,
	0x001FE000, 0x00200000, 0x0000801F, 0x0000C01F, 
	0x001FE000, 0x00400000, 0x0001001F, 0x0001401F,
	0x001FE000, 0x00600000, 0x0001801F, 0x0001C01F,
	0x007FE000,	0x00800000, 0x0002001F, 0x0003001F, 
	0x007FE000, 0x01000000,	0x0004001F, 0x0005001F,	
	0x007FE000, 0x01800000, 0x0006001F, 0x0007001F,
	0x0007E000, 0x20080000, 0x00002017, 0x00003017,
	0x0007E000, 0x20100000, 0x00004017, 0x00005017,
	0x0007E000, 0x20180000, 0x00006017, 0x00007017,
	0x001FE000,	0x20200000, 0x00008017, 0x0000C017,
	0x001FE000, 0x20400000, 0x00010017, 0x00014017,
	0x001FE000, 0x20600000, 0x00018017, 0x0001C017,
	0x007FE000, 0x20800000, 0x00020017, 0x00030017,
	0x007FE000,	0x21000000, 0x00040017, 0x00050017,
	0x007FE000,	0x21800000, 0x00060017, 0x00070017,
};

u32 tlb3_data[] = {
	0x0007E000, 0x30100000, 0x0000403F, 0x0000503F, 
	0x0007E000, 0x30180000, 0x0000603F, 0x0000703F, 
	0x001FE000,	0x30200000, 0x0000803F, 0x0000C03F,
	0x001FE000,	0x30400000, 0x0001003F, 0x0001403F, 
	0x001FE000,	0x30600000, 0x0001803F, 0x0001C03F, 
	0x007FE000, 0x30800000, 0x0002003F, 0x0003003F,	
	0x007FE000, 0x31000000, 0x0004003F, 0x0005003F, 
	0x007FE000, 0x31800000, 0x0006003F, 0x0007003F,
};

u32 tlb_config[] = {
	0xD, 0x12, 8, 0,
	0x80012708, 0x800127D8, 0x800128F8
};


void _TlbSet(u32 Index, u32 PageMask, u32 EntryHi, u32 EntryLo0, u32 EntryLo1) {
	__asm__ (
		"mtc0 $4, $0\n"
		"mtc0 $5, $5\n"
		"mtc0 $6, $10\n"
		"mtc0 $7, $2\n"
		"mtc0 $8, $3\n"
		"sync\n"
		"tlbwi\n"
		"sync\n"
	);
}

void TlbInit() {
	int i, last;
	u32 *ptr;

	__printf("TlbInit... ");

	__asm__ ("mtc0 $0, $6\n");

	_TlbSet(1, 0, 0xE0000000, 0, 0);
	for (i=2; i<48; i++) {
		_TlbSet(i, 0, 0xE0002000, 0, 0);
	}

	last = tlb_config[0];
	if (last > 48) {
		__printf("# TLB over flow (1)\n");
	}

	ptr = (u32*)(tlb_config[4] + 0x10);
	for (i=1; i<last; i++, ptr+= 4) {
		_TlbSet(i, ptr[0], ptr[1], ptr[2], ptr[3]);
	}

	last = tlb_config[1] + i;
	if (last > 48) {
		__printf("# TLB over flow (2)\n");
	}

	if (tlb_config[1]) {
		ptr = (u32*)(tlb_config[5]);
		for (; i<last; i++, ptr+= 4) {
			_TlbSet(i, ptr[0], ptr[1], ptr[2], ptr[3]);
		}
	}

	__asm__ (
		"mtc0 %0, $6\n"
		"sync\n"
		: : "r"(tlb_config[3])
	);
//	if (tlb_config[2] <= 0)	return;

	last = tlb_config[2] + i;
	if (last > 48) {
		__printf("# TLB over flow (3)\n");
	}

	ptr = (u32*)(tlb_config[6]);
	for (; i<last; i++, ptr+= 4) {
		_TlbSet(i, ptr[0], ptr[1], ptr[2], ptr[3]);
	}
	__printf("Ok\n");
}

void DefaultINTCHandler(int n);
void DefaultDMACHandler(int n);
void rcnt3Handler();
void sbusHandler();

////////////////////////////////////////////////////////////////////
//80002050
////////////////////////////////////////////////////////////////////
int InitPgifHandler() {
	int i;

	_HandlersCount = 0;
    
	for (i=0; i<15; i++) {
		intcs_array[i].count = 0;
        // intcs_array[-1] = ihandlers_last,first
		intcs_array[i-1].l.prev = (struct ll*)&intcs_array[i-1].l.next;
		intcs_array[i-1].l.next = (struct ll*)&intcs_array[i-1].l.next;
        setINTCHandler(i, DefaultINTCHandler);
	}

    setINTCHandler(12, rcnt3Handler);
    setINTCHandler(1,  sbusHandler);

	for (i=0; i<32; i++) {
		sbus_handlers[i] = 0;
	}
	
	__EnableIntc(INTC_SBUS);
	
	for (i=0; i<16; i++) {
		dmacs_array[i].count = 0;
		dmacs_array[i-1].l.prev = (struct ll*)&dmacs_array[i-1].l.next;
		dmacs_array[i-1].l.next = (struct ll*)&dmacs_array[i-1].l.next;
        setDMACHandler(i, DefaultDMACHandler);
	}

	handler_ll_free.next = &handler_ll_free;
	handler_ll_free.prev = &handler_ll_free;
	for (i=0; i<160; i++) {
		pgifhandlers_array[i+1].handler = 0;
		pgifhandlers_array[i+1].flag    = 3;
		LL_add(&handler_ll_free, (struct ll*)&pgifhandlers_array[i+1].next);
	}

	return 0x81;
}

////////////////////////////////////////////////////////////////////
//800021B0
// asme as InitPgifHandler except don't reset sbus
////////////////////////////////////////////////////////////////////
int InitPgifHandler2()
{
	int i;

	_HandlersCount = 0;
    
    for(i = 0; i < 15; ++i) {
        if(i != 1 ) {
            intcs_array[i].count = 0;
            // intcs_array[-1] = ihandlers_last,first
            intcs_array[i-1].l.prev = (struct ll*)&intcs_array[i-1].l.next;
            intcs_array[i-1].l.next = (struct ll*)&intcs_array[i-1].l.next;
            setINTCHandler(i, DefaultINTCHandler);
        }
	}

    setINTCHandler(12, rcnt3Handler);
	
	for (i=0; i<16; i++) {
		dmacs_array[i].count = 0;
		dmacs_array[i-1].l.prev = (struct ll*)&dmacs_array[i-1].l.next;
		dmacs_array[i-1].l.next = (struct ll*)&dmacs_array[i-1].l.next;
        setDMACHandler(i, DefaultDMACHandler);
	}

	handler_ll_free.next = &handler_ll_free;
	handler_ll_free.prev = &handler_ll_free;
	for (i=0; i<160; i++) {
		pgifhandlers_array[i+1].handler = 0;
		pgifhandlers_array[i+1].flag    = 3;
		LL_add(&handler_ll_free, (struct ll*)&pgifhandlers_array[i+1].next);
	}

	return 0x81;
}