/*
================================ COPYRIGHT ================================
The contents of this file are subject to the VTech Informations Ltd. License
of VT-OS Ver. 1.1 operating system (the "License"); you may not use this 
file except in compliance with the License.  

Software distributed under the License is distributed on an "AS IS" basis, 
WITHOUT WARRANTY OF ANY KIND, either express or implied.  See the License 
for the specific language governing rights and limitations under the License.
  
The Original Code is VT-OS Ver. 1.1 Operating System, released 
on October 1st, 1999
	
The Initial Developer of the Original Code is VTech Informations Ltd.  All 
codes are Copyright (C) VTech Informations Ltd. 1999.  All Rights Reserved.
===========================================================================
*/

/*
===========================================================================
File        :   intc.c
Author(s)   :   Thomas Cheng
Company     :   VTech Informations Ltd.
Project     :   Helio 
Date:	    :   October 1st, 1999
Purpose:	:   Interrupt solving
Revision    :   1.1
Note        :   None
===========================================================================
*/              



#include "pr3910.h"
#include "intc.h"
#include "tlb.h"
#include "kernel.H"
//#define DEBUG

extern UWORD *app_page, *sys_page, *int_page;

extern int_lookup_tbl int_lookup_table[HOOKABLE_ADDR];

extern INT1, INT2, INT3, INT4, INT5;

HOOKTABLE int_hook_table[512];
UWORD int_priority_mask[INT_MAX_PRIORITY][5];


/*	Function	: UtlbLoadExcHandle
*	Purpose		: Handle UTLB load exception
*  Scope		: OS
*	Input		: None
*	Output      : None
*	Comment		:
*/
void UtlbLoadExcHandle()
{
	UWORD badvpn, page_table;
#ifdef  DEBUG
	printf("\nUtlb Load Exception");
	printf("\nEPC %08x", CP0ReadEPCReg());
	printf("\nBadVAddr %08x", CP0ReadBadVAddrReg());
#endif
	badvpn = TlbGetBadVPN();
	if (badvpn >= 0x40000)
		page_table = sys_page[badvpn & 0xFFFF] & 0x1FFFFFFF;
	else if (badvpn >= 0x30000)
	{
#ifdef DEBUG
		printf("\nBadVpn = %08x ", badvpn);
		printf("\nInt index = %d ", (badvpn>>8)&0x000000FF);
#endif
		int_page = int_lookup_table[(badvpn>>8)&0x000000FF].page_table;
#ifdef DEBUG
		printf("\npage talbe = %08x ", int_page);
#endif
		//                page_table = int_page[badvpn & 0x00FF] & 0x1FFFFFFF;
#ifdef DEBUG
		
		printf("\n page %d ",  badvpn & 0x00FF);
		
		printf(" phy addr %08x " , int_page[badvpn & 0x00FF]);
		
		
#endif
		
		page_table = int_page[badvpn & 0x00FF] & 0x1FFFFFFF;
		//                printf("\n page %d phy addr %08x ", badvpn & 0xFFFF, int_page[badvpn & 0xFFFF]);
	}
	else if (badvpn >= 0x10000)
		page_table = app_page[badvpn & 0xFFFF] & 0x1FFFFFFF;
	else
	{
		CPU->REG_MFIO_DATA_OUT |= 2;
#ifdef DEBUG
		printf("\nUtlb Load Exception");
		printf("\nEPC %08x", CP0ReadEPCReg());
		printf("\nBadVAddr %08x", CP0ReadBadVAddrReg());
#endif
		LcdShowExceptionCode(CP0ReadEPCReg(), 20);
		while(1);
	}
	//	printf("\n %08x %08x", badvpn<<12, page_table);
	TlbSetRandEntry((badvpn<<12), (UWORD)page_table|0x700);
}

void UtlbStoreExcHandle()
{
	UWORD badvpn, page_table;
#ifdef	DEBUG
	printf("\nUtlb Store Exception");
	printf("\nEPC %08x", CP0ReadEPCReg());
	printf("\nBadVAddr %08x", CP0ReadBadVAddrReg());
#endif
	badvpn = TlbGetBadVPN();
	if (badvpn >= 0x40000)
		page_table = sys_page[badvpn & 0xFFFF] & 0x1FFFFFFF;
	else if (badvpn >= 0x30000)
		page_table = int_page[badvpn & 0xFFFF] & 0x1FFFFFFF;
	else if (badvpn >= 0x10000)
		page_table = app_page[badvpn & 0xFFFF] & 0x1FFFFFFF;
	else
	{
		CPU->REG_MFIO_DATA_OUT |= 2;
#ifdef DEBUG
		printf("\nUtlb Store Exception");
		printf("\nEPC %08x", CP0ReadEPCReg());
		printf("\nBadVAddr %08x", CP0ReadBadVAddrReg());
#endif
		LcdShowExceptionCode(CP0ReadEPCReg(), 21);
		while(1);
	}
	//	printf("\n %08x %08x", badvpn<<12, page_table);
	TlbSetRandEntry((badvpn<<12), (UWORD)page_table|0x700);
}

void OtherGenExcHandle()
{
#ifdef DEBUG
	printf("\nNot Handled General Exception");
	printf("\nEPC %08x", CP0ReadEPCReg());
	printf("\nBadVAddr %08x", CP0ReadBadVAddrReg());
#endif
	switch (CP0ReadCauseReg() & CAUSE_EXCMASK)
	{
	case EXC_MOD:
		printf("\nTLB modification exception");
		break;
	case EXC_TLBL:
		printf("\nTLB refill exception - load or instruction fetch");
		break;
	case EXC_TLBS:
		printf("\nTLB exception - store");
		break;
	case EXC_ADEL:
		printf("\nAddress error exception (load or instruction fetch)");
		break;
	case EXC_ADES:
		printf("\nAddress error exception (store)");
		break;
	case EXC_IBE:
		printf("\nBus error exception (instruction fetch)");
		break;
	case EXC_DBE:
		printf("\nBus error exception (data load opt store)");
		break;
	case EXC_BP:
		printf("\nBreakpoint exception (instruction breakpoint)");
		break;
	case EXC_RI:
		printf("\nReserved instruction exception");
		break;
	case EXC_CPU:
		printf("\nCoprocessor unusable");
		break;
	case EXC_OVF:
		printf("\nArithmetic overflow exception");
		break;
	}
	LcdShowExceptionCode(CP0ReadEPCReg(), (CP0ReadCauseReg() & CAUSE_EXCMASK)>>2);
	/*        printf("\nContent at address %08x is %08x", CP0ReadEPCReg() - 4, *(UWORD *)(CP0ReadEPCReg() - 4));
	printf("\nContent at address %08x is %08x", CP0ReadEPCReg()    , *(UWORD *)CP0ReadEPCReg());
	while(1);
	*/
}

void OtherIntHighHandle()
{
	printf("\nNot Handled High Priority Interrupt");
	printf("\nEPC %08x", CP0ReadEPCReg());
	printf("\nInterrupt Status Register 6 %08x", CPU->REG_INT_STATUS_6);
	LcdShowExceptionCode(CP0ReadEPCReg(), 30);
	while(1);
}

void OtherIntLow1Handle()
{
	printf("\nNot Handled Low Priority Interrupt");
	printf("\nEPC %08x", CP0ReadEPCReg());
	printf("\nInterrupt Status Register 1 %08x", CHK_INT_1);
	LcdShowExceptionCode(CP0ReadEPCReg(), 31);
	while(1);
}

void OtherIntLow2Handle()
{
	printf("\nNot Handled Low Priority Interrupt");
	printf("\nEPC %08x", CP0ReadEPCReg());
	printf("\nInterrupt Status Register 2 %08x", CHK_INT_2);
	LcdShowExceptionCode(CP0ReadEPCReg(), 32);
	while(1);
}

void OtherIntLow3Handle()
{
	printf("\nNot Handled Low Priority Interrupt");
	printf("\nEPC %08x", CP0ReadEPCReg());
	printf("\nInterrupt Status Register 3 %08x", CHK_INT_3);
	LcdShowExceptionCode(CP0ReadEPCReg(), 33);
	while(1);
}

void OtherIntLow4Handle()
{
	printf("\nNot Handled Low Priority Interrupt");
	printf("\nEPC %08x", CP0ReadEPCReg());
	printf("\nInterrupt Status Register 4 %08x", CHK_INT_4);
	LcdShowExceptionCode(CP0ReadEPCReg(), 34);
	while(1);
}

void OtherIntLow5Handle()
{
	printf("\nNot Handled Low Priority Interrupt");
	printf("\nEPC %08x", CP0ReadEPCReg());
	printf("\nInterrupt Status Register 5 %08x", CHK_INT_5);
	LcdShowExceptionCode(CP0ReadEPCReg(), 35);
	while(1);
}

void IntInit()
{
	UWORD i, j;
	for (i = 0; i<512; i++)
	{
		int_hook_table[i].ISRID = 0;
		int_hook_table[i].offset = 0xFFFFFFFF;
		int_hook_table[i].active = 0;
		int_hook_table[i].priority = 0;
		int_hook_table[i].next_ptr = 0;
	}
	for (i = 0; i<INT_MAX_PRIORITY; i++)
		for (j = 0; j<5; j++)
			int_priority_mask[i][j] = 0;
		CLR_INT_ALL;
		DIS_INT_ALL;
		//    ENA_GBL_INT;
}

/*	Function	: IntEnableGlobal
*	Purpose		: Enable the Global interrupt
*  Scope		: OS
*	Input		: None
*	Output      : None
*	Comment		: Interrupt nesting with 5 levels of priority
*/
void IntEnableGlobal()
{
	CP0WriteStatusReg(CP0ReadStatusReg()|STATUS_IEC);
}

/*	Function	: IntDisableGlobal
*	Purpose		: Disable the Global interrupt
*  Scope		: OS
*	Input		: None
*	Output      : None
*	Comment		: Interrupt nesting with 5 levels of priority
*/
void IntDisableGlobal()
{
	CP0WriteStatusReg(CP0ReadStatusReg()&(~STATUS_IEC));
}

/*	Function	: IntSetPriority
*	Purpose		: Set the priority of interrupt
*  Scope		: OS
*	Input		: Interrupt number : 0 - 144
Priority of interrupt : 0(lowest) - 4(highest)
*	Output      : None
*	Comment		: Remember to realize it by running IntBuildPriorityMask()
*/
void IntSetPriority(USHORT int_no, USHORT priority)
{
	if (priority < INT_MAX_PRIORITY)
	{
		int_hook_table[int_no].priority = priority;
	}
}

/*	Function	: IntBuildPriorityMask
*	Purpose		: Build the priority mask of interrupt
*  Scope		: OS
*	Input		: None
*	Output      : None
*	Comment		:
*/
void IntBuildPriorityMask()
{
	UWORD i, j;
	for (i=0; i<5; i++)
	{
		for (j=0; j<27; j++)
		{
			if (int_hook_table[j].priority > i)
				int_priority_mask[i][0] |= 1 << (31-j);
		}
		for (j=27; j<47; j++)
		{
			if (int_hook_table[j].priority > i)
				int_priority_mask[i][1] |= 1 << (58-j);
		}
		for (j=47; j<79; j++)
		{
			if (int_hook_table[j].priority > i)
				int_priority_mask[i][2] |= 1 << (78-j);
		}
		for (j=79; j<111; j++)
		{
			if (int_hook_table[j].priority > i)
				int_priority_mask[i][3] |= 1 << (110-j);
		}
		for (j=111; j<143; j++)
		{
			if (int_hook_table[j].priority > i)
				int_priority_mask[i][4] |= 1 << (142-j);
		}
	}
}

/*	Function	: IntMaskIntEnable
*	Purpose		: Mask the interrupt enable register
*  Scope		: OS
*	Input		: Priority of interrupt : 0(lowest) - 4(highest)
*	Output      : None
*	Comment		:
*/
void IntMaskIntEnable(UWORD int_no)
{
	USHORT priority = int_hook_table[int_no].priority;
	if (priority <= INT_MAX_PRIORITY)
	{
		CPU->REG_INT_ENABLE_1 &= int_priority_mask[priority][0];
		CPU->REG_INT_ENABLE_2 &= int_priority_mask[priority][1];
		CPU->REG_INT_ENABLE_3 &= int_priority_mask[priority][2];
		CPU->REG_INT_ENABLE_4 &= int_priority_mask[priority][3];
		CPU->REG_INT_ENABLE_5 &= int_priority_mask[priority][4];
	}
}

void ChkPt(UWORD a, UWORD b, UWORD c)
{
	printf("\nChkPt %08x %08x %d", a, b, c);
	/* 	printf("\nCP0Cause %08x", CP0ReadCauseReg());
	printf("\nCP0Status %08x", CP0ReadStatusReg());
	printf("\nInterrupt1 %08x", CPU->REG_INT_ENABLE_1 & INT1);
	printf("\nInterrupt2 %08x", CPU->REG_INT_ENABLE_2 & INT2);
	printf("\nInterrupt3 %08x", CPU->REG_INT_ENABLE_3 & INT3);
	printf("\nInterrupt4 %08x", CPU->REG_INT_ENABLE_4 & INT4);
	printf("\nInterrupt5 %08x", CPU->REG_INT_ENABLE_5 & INT5);
	
	  /* 	printf("\nInterrupt1 %08x", CPU->REG_INT_ENABLE_1 & CPU->REG_INT_STATUS_1);
	  printf("\nInterrupt2 %08x", CPU->REG_INT_ENABLE_2 & CPU->REG_INT_STATUS_2);
	  printf("\nInterrupt3 %08x", CPU->REG_INT_ENABLE_3 & CPU->REG_INT_STATUS_3);
	  printf("\nInterrupt4 %08x", CPU->REG_INT_ENABLE_4 & CPU->REG_INT_STATUS_4);
	printf("\nInterrupt5 %08x", CPU->REG_INT_ENABLE_5 & CPU->REG_INT_STATUS_5);*/
}

void PrintRA(UWORD a)
{
	printf("\nRa = %08x", a);
}

/*	Function	: SysSearchLastHookCell
*	Purpose		: Search for last hook cell
*  Scope		: Internal
*	Input		: Interrupt number
*	Output      : Hook number of last cell
*	Comment		:
*/
USHORT IntSearchLastHookCell(USHORT int_no)
{
	if (int_hook_table[int_no].offset == 0xFFFFFFFF)
		return 0xFFFF;
	while(int_hook_table[int_no].next_ptr != 0)
	{
		int_no = int_hook_table[int_no].next_ptr;
	}
	return int_no;
}


/*	Function	: SysSearchFreeHookCell
*	Purpose		: Search for free hook cell
*  Scope		: Internal
*	Input		: Interrupt number
*	Output      : Hook number of free cell
*	Comment		:
*/
USHORT IntSearchFreeHookCell(USHORT int_no)
{
	if (int_hook_table[int_no].offset == 0xFFFFFFFF)
		return int_no;
	for (int_no=143; int_no<512; int_no++)
	{
		if (int_hook_table[int_no].offset == 0xFFFFFFFF)
			break;
	}
	return int_no;
}

/*	Function	: SysSearchNextPtr
*	Purpose		: Search for next hook cell
*  Scope		: Internal
*	Input		: Hook number
*	Output      : Hook number of next cell
*	Comment		:
*/
USHORT IntSearchNextPtr(USHORT hook_no)
{
	UWORD int_no;
	for (int_no=0; int_no<512; int_no++)
	{
		if (int_hook_table[int_no].next_ptr == hook_no)
			break;
	}
	return int_no;
}

USHORT IntHookISR(USHORT ISRID, USHORT int_no, UWORD offset)
{
	USHORT last_no, hook_no;
	
	DIS_GBL_INT;
	
	last_no = IntSearchLastHookCell(int_no);
	
	hook_no = IntSearchFreeHookCell(int_no);
	
	int_hook_table[hook_no].ISRID = ISRID;
	int_hook_table[hook_no].offset = offset;
	
	if (last_no != 0xFFFF)
	{
		int_hook_table[last_no].next_ptr = hook_no;
	}
	
	ENA_GBL_INT;
	
	return hook_no;
}

void IntUnhookISR(USHORT hook_no)
{
	DIS_GBL_INT;
	int_hook_table[hook_no].ISRID = 0;
	int_hook_table[hook_no].offset = 0xFFFFFFFF;
	if (hook_no >= 143)
	{
		int_hook_table[IntSearchNextPtr(hook_no)].next_ptr = int_hook_table[hook_no].next_ptr;
		int_hook_table[hook_no].next_ptr = 0;
	}
	ENA_GBL_INT;
}

void IntUnhookISRbyAppID(USHORT appid)
{
	UWORD i;
	
	for(i=0;i<512;i++)
	{
		if(int_hook_table[i].ISRID == appid)
			IntUnhookISR(i);
	}
}

void IntSetHookAddr(UBYTE addr, UWORD *ptable, AppID appid)
{
	int_lookup_table[addr].page_table = ptable;
	int_lookup_table[addr].appid = appid;
}

void IntClearHookAddr(UBYTE addr)
{
	int_lookup_table[addr].page_table = NULL;
	int_lookup_table[addr].appid = 0;
	int_hook_table[addr].active = 0;
}

void IntRunHookedISR(USHORT int_no, BYTE *arg)
{
	
#ifdef DEBUG
	printf("\nIsr# %08x ", int_no);
#endif
	
	if (int_hook_table[int_no].offset == 0xFFFFFFFF && int_hook_table[int_no].next_ptr != 0)
		int_no = int_hook_table[int_no].next_ptr;
	
	if (int_hook_table[int_no].offset != 0xFFFFFFFF)
		do
		{
			if (int_hook_table[int_no].active == 1)
			{
				//                                SysLoadInterruptPageTable(int_hook_table[int_no].ISRID);
#ifdef DEBUG
				printf("\nB4 runISR");
				printf("\n offset = %08x ", int_hook_table[int_no].offset);
#endif
				RunISR(arg, int_hook_table[int_no].offset);
#ifdef DEBUG
				printf("\nAfter runISR");
#endif
				
				//                                SysClearInterruptData(1);
				//                                SysClearInterruptData(int_hook_table[int_no].ISRID);
			}
			if (int_hook_table[int_no].next_ptr == 0)
				break;
			int_no = int_hook_table[int_no].next_ptr;
		} while (1);
}

void IntSetActive(USHORT hook_no)
{
	int_hook_table[hook_no].active = 1;
}

UBYTE IntGetActive(USHORT hook_no)
{
	return int_hook_table[hook_no].active;
}

