sh4_interrupts.cpp 6.65 KB
Newer Older
1
/*
2
	Interrupt list caching and handling
3

4
	SH4 has a very flexible interrupt controller. In order to handle it efficiently, a sorted
5
6
	interrupt bitfield is build from the set interrupt priorities. Higher priorities get allocated
	into higher bits, and a simple mask is kept. In order to check for pending interrupts a simple
7
	!=0 test works, and to identify the pending interrupt bsr(pend) will give the sorted id. As
8
9
10
11
12
13
14
15
	this is a single cycle operation on most platforms, the interrupt checking/identification
	is very fast !
*/

#include "types.h"
#include "sh4_interrupts.h"
#include "sh4_core.h"
#include "sh4_mmr.h"
16
#include "oslib/oslib.h"
17
18

/*
19

20
21
22
*/

//these are fixed
Flyinghead's avatar
Flyinghead committed
23
const u16 IRLPriority = 0x0246;
24
25
26
27
28
29
30
31
#define IRLP9 &IRLPriority,0
#define IRLP11 &IRLPriority,4
#define IRLP13 &IRLPriority,8

#define GIPA(p) &INTC_IPRA.reg_data,4*p
#define GIPB(p) &INTC_IPRB.reg_data,4*p
#define GIPC(p) &INTC_IPRC.reg_data,4*p

Flyinghead's avatar
Flyinghead committed
32
33
34
35
36
37
38
39
struct InterptSourceList_Entry
{
	const u16* PrioReg;
	u32 Shift;
	u32 IntEvnCode;

	u32 GetPrLvl() const { return ((*PrioReg)>>Shift)&0xF; }
};
40
41
42
43
44

//Can't be statically initialised because registers are dynamically allocated
InterptSourceList_Entry InterruptSourceList[28];

//Maps siid -> EventID
45
DECL_ALIGN(64) u16 InterruptEnvId[32] = { 0 };
46
//Maps piid -> 1<<siid
47
DECL_ALIGN(64) u32 InterruptBit[32] = { 0 };
48
//Maps sh4 interrupt level to inclusive bitfield
49
DECL_ALIGN(64) u32 InterruptLevelBit[16] = { 0 };
50

Flyinghead's avatar
Flyinghead committed
51
bool Do_Interrupt(u32 intEvn);
52
bool Do_Exception(u32 epc, u32 expEvn, u32 CallVect);
53

54
55
56
u32 interrupt_vpend; // Vector of pending interrupts
u32 interrupt_vmask; // Vector of masked interrupts             (-1 inhibits all interrupts)
u32 decoded_srimask; // Vector of interrupts allowed by SR.IMSK (-1 inhibits all interrupts)
57
58

//bit 0 ~ 27 : interrupt source 27:0. 0 = lowest level, 27 = highest level.
59
static void recalc_pending_itrs(void)
60
61
62
63
64
{
	Sh4cntx.interrupt_pend=interrupt_vpend&interrupt_vmask&decoded_srimask;
}

//Rebuild sorted interrupt id table (priorities were updated)
65
void SIIDRebuild(void)
66
67
68
69
70
71
72
73
74
{
	u32 cnt=0;
	u32 vpend=interrupt_vpend;
	u32 vmask=interrupt_vmask;
	interrupt_vpend=0;
	interrupt_vmask=0x00000000;
	//rebuild interrupt table
	for (u32 ilevel=0;ilevel<16;ilevel++)
	{
Flyinghead's avatar
Flyinghead committed
75
76
77
78
79
80
81
82
83
84
85
86
87
88
	   for (u32 isrc=0;isrc<28;isrc++)
       {
	      if (InterruptSourceList[isrc].GetPrLvl()==ilevel)
	      {
             InterruptEnvId[cnt]=InterruptSourceList[isrc].IntEvnCode;
             u32 p=InterruptBit[isrc]&vpend;
             u32 m=InterruptBit[isrc]&vmask;
             InterruptBit[isrc]=1<<cnt;
             if (p)
                interrupt_vpend|=InterruptBit[isrc];
             if (m)
                interrupt_vmask|=InterruptBit[isrc];
             cnt++;
          }
89
      }
Flyinghead's avatar
Flyinghead committed
90
      InterruptLevelBit[ilevel]=(1<<cnt)-1;
91
92
93
94
95
96
	}

	SRdecode();
}

//Decode SR.IMSK into a interrupt mask, update and return the interrupt state
97
bool SRdecode(void)
98
{
99
   decoded_srimask = (sr.BL) ? ~0xFFFFFFFF : ~InterruptLevelBit[sr.IMASK];
100
101
102
103
104

	recalc_pending_itrs();
	return Sh4cntx.interrupt_pend;
}

105
int UpdateINTC(void)
106
{
107
108
109
	if (Sh4cntx.interrupt_pend)
      return Do_Interrupt(InterruptEnvId[bitscanrev(Sh4cntx.interrupt_pend)]);
   return 0;
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
}

void SetInterruptPend(InterruptID intr)
{
	u32 piid= intr & InterruptPIIDMask;
	interrupt_vpend|=InterruptBit[piid];
	recalc_pending_itrs();
}
void ResetInterruptPend(InterruptID intr)
{
	u32 piid= intr & InterruptPIIDMask;
	interrupt_vpend&=~InterruptBit[piid];
	recalc_pending_itrs();
}

void SetInterruptMask(InterruptID intr)
{
	u32 piid= intr & InterruptPIIDMask;
	interrupt_vmask|=InterruptBit[piid];
	recalc_pending_itrs();
}
void ResetInterruptMask(InterruptID intr)
{
	u32 piid= intr & InterruptPIIDMask;
	interrupt_vmask&=~InterruptBit[piid];
	recalc_pending_itrs();
}


Flyinghead's avatar
Flyinghead committed
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
bool Do_Interrupt(u32 intEvn)
{
	CCN_INTEVT = intEvn;

	ssr = sh4_sr_GetFull();
	spc = next_pc;
	sgr = r[15];
	sr.BL = 1;
	sr.MD = 1;
	sr.RB = 1;
	UpdateSR();
	next_pc = vbr + 0x600;

	return true;
}

155
bool Do_Exception(u32 epc, u32 expEvn, u32 CallVect)
156
{
157
	verify(sr.BL == 0);
158
159
	CCN_EXPEVT = expEvn;

Sven's avatar
Sven committed
160
	ssr = sh4_sr_GetFull();
161
162
163
164
165
166
167
168
	spc = epc;
	sgr = r[15];
	sr.BL = 1;
	sr.MD = 1;
	sr.RB = 1;
	UpdateSR();

	next_pc = vbr + CallVect;
169
	//printf("RaiseException: from %08X , pc errh %08X, %08X vect\n", spc, epc, next_pc);
170
171
172
173
174

	return true;
}

//Init/Res/Term
175
void interrupts_init(void)
176
177
178
179
{
	InterptSourceList_Entry InterruptSourceList2[]=
	{
		//IRL
180
181
182
		{IRLP9,0x320},//sh4_IRL_9           = KMIID(sh4_int,0x320,0),
		{IRLP11,0x360},//sh4_IRL_11         = KMIID(sh4_int,0x360,1),
		{IRLP13,0x3A0},//sh4_IRL_13         = KMIID(sh4_int,0x3A0,2),
183
184

		//HUDI
185
		{GIPC(0),0x600},//sh4_HUDI_HUDI     = KMIID(sh4_int,0x600,3),  /* H-UDI underflow */
186
187

		//GPIO (missing on dc ?)
188
		{GIPC(3),0x620},//sh4_GPIO_GPIOI    = KMIID(sh4_int,0x620,4),
189
190

		//DMAC
191
192
193
194
195
		{GIPC(2),0x640},//sh4_DMAC_DMTE0    = KMIID(sh4_int,0x640,5),
		{GIPC(2),0x660},//sh4_DMAC_DMTE1    = KMIID(sh4_int,0x660,6),
		{GIPC(2),0x680},//sh4_DMAC_DMTE2    = KMIID(sh4_int,0x680,7),
		{GIPC(2),0x6A0},//sh4_DMAC_DMTE3    = KMIID(sh4_int,0x6A0,8),
		{GIPC(2),0x6C0},//sh4_DMAC_DMAE     = KMIID(sh4_int,0x6C0,9),
196
197

		//TMU
198
199
200
201
		{GIPA(3),0x400},//sh4_TMU0_TUNI0    =  KMIID(sh4_int,0x400,10), /* TMU0 underflow */
		{GIPA(2),0x420},//sh4_TMU1_TUNI1    =  KMIID(sh4_int,0x420,11), /* TMU1 underflow */
		{GIPA(1),0x440},//sh4_TMU2_TUNI2    =  KMIID(sh4_int,0x440,12), /* TMU2 underflow */
		{GIPA(1),0x460},//sh4_TMU2_TICPI2   =  KMIID(sh4_int,0x460,13),
202
203

		//RTC
204
205
206
		{GIPA(0),0x480},//sh4_RTC_ATI       = KMIID(sh4_int,0x480,14),
		{GIPA(0),0x4A0},//sh4_RTC_PRI       = KMIID(sh4_int,0x4A0,15),
		{GIPA(0),0x4C0},//sh4_RTC_CUI       = KMIID(sh4_int,0x4C0,16),
207
208

		//SCI
209
210
211
212
		{GIPB(1),0x4E0},//sh4_SCI1_ERI      = KMIID(sh4_int,0x4E0,17),
		{GIPB(1),0x500},//sh4_SCI1_RXI      = KMIID(sh4_int,0x500,18),
		{GIPB(1),0x520},//sh4_SCI1_TXI      = KMIID(sh4_int,0x520,19),
		{GIPB(1),0x540},//sh4_SCI1_TEI      = KMIID(sh4_int,0x540,29),
213
214

		//SCIF
215
216
217
218
		{GIPC(1),0x700},//sh4_SCIF_ERI      = KMIID(sh4_int,0x700,21),
		{GIPC(1),0x720},//sh4_SCIF_RXI      = KMIID(sh4_int,0x720,22),
		{GIPC(1),0x740},//sh4_SCIF_BRI      = KMIID(sh4_int,0x740,23),
		{GIPC(1),0x760},//sh4_SCIF_TXI      = KMIID(sh4_int,0x760,24),
219
220

		//WDT
221
		{GIPB(3),0x560},//sh4_WDT_ITI       = KMIID(sh4_int,0x560,25),
222
223

		//REF
224
225
		{GIPB(2),0x580},//sh4_REF_RCMI      = KMIID(sh4_int,0x580,26),
		{GIPA(2),0x5A0},//sh4_REF_ROVI      = KMIID(sh4_int,0x5A0,27),
226
227
228
229
230
231
232
	};

	verify(sizeof(InterruptSourceList)==sizeof(InterruptSourceList2));

	memcpy(InterruptSourceList,InterruptSourceList2,sizeof(InterruptSourceList));
}

233
void interrupts_reset(void)
234
235
236
237
238
239
240
241
242
243
244
245
246
{
	//reset interrupts cache
	interrupt_vpend=0x00000000;
	interrupt_vmask=0xFFFFFFFF;
	decoded_srimask=0;

	for (u32 i=0;i<28;i++)
		InterruptBit[i]=1<<i;

	//rebuild the interrupts table
	SIIDRebuild();
}

247
void interrupts_term(void)
248
249
250
{

}