1 //---------------------------------------------------------------------------
2 // NEOPOP : Emulator as in Dreamland
3 //
4 // Copyright (c) 2001-2002 by neopop_uk
5 //---------------------------------------------------------------------------
6 
7 //---------------------------------------------------------------------------
8 //	This program is free software; you can redistribute it and/or modify
9 //	it under the terms of the GNU General Public License as published by
10 //	the Free Software Foundation; either version 2 of the License, or
11 //	(at your option) any later version. See also the license.txt file for
12 //	additional informations.
13 //---------------------------------------------------------------------------
14 
15 #include "neopop.h"
16 #include "mem.h"
17 #include "gfx.h"
18 #include "interrupt.h"
19 #include "Z80_interface.h"
20 #include "dma.h"
21 
22 //=============================================================================
23 
24 namespace MDFN_IEN_NGP
25 {
26 
27 uint32 timer_hint;
28 static uint32 timer_clock[4];
29 static uint8 timer[4];	//Up-counters
30 static uint8 timer_threshold[4];
31 
32 static uint8 TRUN;
33 static uint8 T01MOD, T23MOD;
34 static uint8 TRDC;
35 static uint8 TFFCR;
36 static uint8 HDMAStartVector[4];
37 
38 static int32 ipending[24];
39 static int32 IntPrio[0xB]; // 0070-007a
40 static bool h_int, timer0, timer2;
41 
42 // The way interrupt processing is set up is still written towards BIOS HLE emulation, which assumes
43 // that the interrupt handler will immediately call DI, clear the interrupt latch(so the interrupt won't happen again when interrupts are re-enabled),
44 // and then call the game's interrupt handler.
45 
46 // We automatically clear the interrupt latch when the interrupt is accepted, and the interrupt latch is not checked every instruction,
47 // but only when: an EI/DI, POP SR, or RETI instruction occurs; after a write to an interrupt priority register occurs; and when
48 // a device sets the virual interrupt latch register, signaling it wants an interrupt.
49 
50 // FIXME in the future if we ever add real bios support?
interrupt(const uint8 index,const int level)51 void interrupt(const uint8 index, const int level)
52 {
53 	//printf("INT: %d\n", index);
54 	push32(pc);
55 	push16(sr);
56 
57 	//Up the IFF
58 	if(level >= 0)
59 	 setStatusIFF((level < 7) ? (level + 1) : 7);
60 
61         //Access the interrupt vector table to find the jump destination
62         pc = loadL(0x6FB8 + index * 4);
63 }
64 
set_interrupt(uint8 index,bool set)65 void set_interrupt(uint8 index, bool set)
66 {
67  assert(index < 24);
68 
69  ipending[index] = set;
70  int_check_pending();
71 }
72 
int_check_pending(void)73 void int_check_pending(void)
74 {
75  uint8 prio;
76  uint8 curIFF = statusIFF();
77 
78  // Technically, the BIOS should clear the interrupt pending flag by writing with IxxC set to "0", but
79  // we'll actually need to implement a BIOS to do that!
80 
81  prio = IntPrio[0x1] & 0x07;     // INT4
82  if(ipending[5] && curIFF <= prio && prio && prio != 7)
83  {
84   ipending[5] = 0;
85   interrupt(5, prio);
86   return;
87  }
88 
89  prio = (IntPrio[0x1] & 0x70) >> 4;	// INT5 (Z80)
90  if(ipending[6] && curIFF <= prio && prio && prio != 7)
91  {
92   ipending[6] = 0;
93   interrupt(6, prio);
94   return;
95  }
96 
97  prio = IntPrio[0x3] & 0x07;	// INTT0
98  if(ipending[7] && curIFF <= prio && prio && prio != 7)
99  {
100   ipending[7] = 0;
101   interrupt(7, prio);
102   return;
103  }
104 
105  prio = (IntPrio[0x3] & 0x70) >> 4;	// INTT1
106  if(ipending[8] && curIFF <= prio && prio && prio != 7)
107  {
108   ipending[8] = 0;
109   interrupt(8, prio);
110   return;
111  }
112 
113  prio = (IntPrio[0x4] & 0x07);	// INTT2
114  if(ipending[9] && curIFF <= prio && prio && prio != 7)
115  {
116   ipending[9] = 0;
117   interrupt(9, prio);
118   return;
119  }
120 
121  prio = ((IntPrio[0x4] & 0x70) >> 4); // INTT3
122  if(ipending[10] && curIFF <= prio && prio && prio != 7)
123  {
124   ipending[10] = 0;
125   interrupt(10, prio);
126   return;
127  }
128 
129  prio = (IntPrio[0x7] & 0x07); // INTRX0
130  if(ipending[11] && curIFF <= prio && prio && prio != 7)
131  {
132   ipending[11] = 0;
133   interrupt(11, prio);
134   return;
135  }
136 
137  prio = ((IntPrio[0x7] & 0x70) >> 4); // INTTX0
138  if(ipending[12] && curIFF <= prio && prio && prio != 7)
139  {
140   ipending[12] = 0;
141   interrupt(12, prio);
142   return;
143  }
144 
145 }
146 
int_write8(uint32 address,uint8 data)147 void int_write8(uint32 address, uint8 data)
148 {
149  switch(address)
150  {
151   case 0x71: if(!(data & 0x08)) ipending[5] = 0;
152              if(!(data & 0x80)) ipending[6] = 0;
153              break;
154   case 0x73: if(!(data & 0x08)) ipending[7] = 0;
155 	     if(!(data & 0x80)) ipending[8] = 0;
156 	     break;
157   case 0x74: if(!(data & 0x08)) ipending[9] = 0;
158 	     if(!(data & 0x80)) ipending[10] = 0;
159 	     break;
160   case 0x77: if(!(data & 0x08)) ipending[11] = 0;
161              if(!(data & 0x80)) ipending[12] = 0;
162              break;
163   case 0x7C: HDMAStartVector[0] = data; break;
164   case 0x7D: HDMAStartVector[1] = data; break;
165   case 0x7E: HDMAStartVector[2] = data; break;
166   case 0x7F: HDMAStartVector[3] = data; break;
167  }
168  if(address >= 0x70 && address <= 0x7A)
169  {
170   IntPrio[address - 0x70] = data;
171   int_check_pending();
172  }
173 }
174 
int_read8(uint32 address)175 uint8 int_read8(uint32 address)
176 {
177  uint8 ret = 0;
178  switch(address)
179  {
180   case 0x71: ret = ((ipending[5] ? 0x08 : 0x00) | (ipending[6] ? 0x80 : 0x00)); break;
181   case 0x73: ret = ((ipending[7] ? 0x08 : 0x00) | (ipending[8] ? 0x80 : 0x00)); break;
182   case 0x74: ret = ((ipending[9] ? 0x08 : 0x00) | (ipending[10] ? 0x80 : 0x00)); break;
183   case 0x77: ret = ((ipending[11] ? 0x08 : 0x00) | (ipending[12] ? 0x80 : 0x00)); break;
184  }
185 
186  return(ret);
187 }
188 
TestIntHDMA(int bios_num,int vec_num)189 void TestIntHDMA(int bios_num, int vec_num)
190 {
191 	bool WasDMA = 0;
192 
193                         if (HDMAStartVector[0] == vec_num)
194                         {
195                          WasDMA = 1;
196                          DMA_update(0);
197                         }
198                         else
199                         {
200                          if (HDMAStartVector[1] == vec_num)
201                          {
202                           WasDMA = 1;
203                           DMA_update(1);
204                          }
205                          else
206                          {
207                           if (HDMAStartVector[2] == vec_num)
208                           {
209                            WasDMA = 1;
210                            DMA_update(2);
211                           }
212                           else
213                           {
214                            if (HDMAStartVector[3] == vec_num)
215                            {
216                             WasDMA = 1;
217                             DMA_update(3);
218                            }
219                           }
220                          }
221                         }
222 	if(!WasDMA)
223          set_interrupt(bios_num, true);
224 }
225 
226 
227 extern int32 ngpc_soundTS;
228 extern bool NGPFrameSkip;
229 
updateTimers(MDFN_Surface * surface,int cputicks)230 bool updateTimers(MDFN_Surface *surface, int cputicks)
231 {
232 	bool ret = 0;
233 
234 	ngpc_soundTS += cputicks;
235 	//increment H-INT timer
236 	timer_hint += cputicks;
237 
238 	//=======================
239 
240 	//End of scanline / Start of Next one
241 	if (timer_hint >= TIMER_HINT_RATE)
242 	{
243 		uint8 data;
244 
245 		// ============= END OF CURRENT SCANLINE =============
246 
247 		h_int = NGPGfx->hint();
248 		ret = NGPGfx->draw(surface, NGPFrameSkip);
249 
250 		// ============= START OF NEXT SCANLINE =============
251 
252 		timer_hint -= TIMER_HINT_RATE;	//Start of next scanline
253 
254 		//Comms. Read interrupt
255 		if ((COMMStatus & 1) == 0 && system_comms_poll(&data))
256 		{
257 			storeB(0x50, data);
258 			TestIntHDMA(12, 0x19);
259 		}
260 	}
261 
262 	//=======================
263 
264 	//Tick the Clock Generator
265 	timer_clock[0] += cputicks;
266 	timer_clock[1] += cputicks;
267 
268 	timer0 = false;	//Clear the timer0 tick, for timer1 chain mode.
269 
270 	//=======================
271 
272 	//Run Timer 0 (TRUN)?
273 	if ((TRUN & 0x01))
274 	{
275 		//T01MOD
276 		switch(T01MOD & 0x03)
277 		{
278 		case 0:	if (h_int)	//Horizontal interrupt trigger
279 				{
280 					timer[0]++;
281 
282 					timer_clock[0] = 0;
283 					h_int = false;	// Stop h_int remaining active
284 				}
285 				break;
286 
287 		case 1:	while (timer_clock[0] >= TIMER_T1_RATE)
288 				{
289 					timer[0]++;
290 					timer_clock[0] -= TIMER_T1_RATE;
291 				}
292 				break;
293 
294 		case 2:	while(timer_clock[0] >= TIMER_T4_RATE)
295 				{
296 					timer[0]++;
297 					timer_clock[0] -= TIMER_T4_RATE;
298 				}
299 				break;
300 
301 		case 3:	while (timer_clock[0] >= TIMER_T16_RATE)
302 				{
303 					timer[0]++;
304 					timer_clock[0] -= TIMER_T16_RATE;
305 				}
306 				break;
307 		}
308 
309 
310 		//Threshold check
311 		if (timer_threshold[0] && timer[0] >= timer_threshold[0])
312 		{
313 			timer[0] = 0;
314 			timer0 = true;
315 
316 			TestIntHDMA(7, 0x10);
317 		}
318 	}
319 
320 	//=======================
321 
322 	//Run Timer 1 (TRUN)?
323 	if ((TRUN & 0x02))
324 	{
325 		//T01MOD
326 		switch((T01MOD & 0x0C) >> 2)
327 		{
328 		case 0:	if (timer0)	//Timer 0 chain mode.
329 				{
330 					timer[1] += timer0;
331 					timer_clock[1] = 0;
332 				}
333 				break;
334 
335 		case 1:	while (timer_clock[1] >= TIMER_T1_RATE)
336 				{
337 					timer[1]++;
338 					timer_clock[1] -= TIMER_T1_RATE;
339 				}
340 				break;
341 
342 		case 2:	while (timer_clock[1] >= TIMER_T16_RATE)
343 				{
344 					timer[1]++;
345 					timer_clock[1] -= TIMER_T16_RATE;
346 				}
347 				break;
348 
349 		case 3:	while (timer_clock[1] >= TIMER_T256_RATE)
350 				{
351 					timer[1]++;
352 					timer_clock[1] -= TIMER_T256_RATE;
353 				}
354 				break;
355 		}
356 
357 		//Threshold check
358 		if (timer_threshold[1] && timer[1] >= timer_threshold[1])
359 		{
360 			timer[1] = 0;
361 
362 			TestIntHDMA(8, 0x11);
363 		}
364 	}
365 
366 	//=======================
367 
368 	//Tick the Clock Generator
369 	timer_clock[2] += cputicks;
370 	timer_clock[3] += cputicks;
371 
372 	timer2 = false;	//Clear the timer2 tick, for timer3 chain mode.
373 
374 	//=======================
375 
376 	//Run Timer 2 (TRUN)?
377 	if ((TRUN & 0x04))
378 	{
379 		//T23MOD
380 		switch(T23MOD & 0x03)
381 		{
382 		case 0:	// -
383 				break;
384 
385 		case 1:	while (timer_clock[2] >= TIMER_T1_RATE / 2) // Kludge :(
386 				{
387 					timer[2]++;
388 					timer_clock[2] -= TIMER_T1_RATE / 2;
389 				}
390 				break;
391 
392 		case 2:	while (timer_clock[2] >= TIMER_T4_RATE)
393 				{
394 					timer[2]++;
395 					timer_clock[2] -= TIMER_T4_RATE;
396 				}
397 				break;
398 
399 		case 3:	while (timer_clock[2] >= TIMER_T16_RATE)
400 				{
401 					timer[2]++;
402 					timer_clock[2] -= TIMER_T16_RATE;
403 				}
404 				break;
405 		}
406 
407 		//Threshold check
408 		if (timer_threshold[2] && timer[2] >= timer_threshold[2])
409 		{
410 			timer[2] = 0;
411 			timer2 = true;
412 
413 			TestIntHDMA(9, 0x12);
414 		}
415 	}
416 
417 	//=======================
418 
419 	//Run Timer 3 (TRUN)?
420 	if ((TRUN & 0x08))
421 	{
422 		//T23MOD
423 		switch((T23MOD & 0x0C) >> 2)
424 		{
425 		case 0:	if(timer2)	//Timer 2 chain mode.
426 				{
427 					timer[3] += timer2;
428 					timer_clock[3] = 0;
429 				}
430 				break;
431 
432 		case 1:	while (timer_clock[3] >= TIMER_T1_RATE)
433 				{
434 					timer[3]++;
435 					timer_clock[3] -= TIMER_T1_RATE;
436 				}
437 				break;
438 
439 		case 2:	while (timer_clock[3] >= TIMER_T16_RATE)
440 				{
441 					timer[3]++;
442 					timer_clock[3] -= TIMER_T16_RATE;
443 				}
444 				break;
445 
446 		case 3:	while (timer_clock[3] >= TIMER_T256_RATE)
447 				{
448 					timer[3]++;
449 					timer_clock[3] -= TIMER_T256_RATE;
450 				}
451 				break;
452 		}
453 
454 		//Threshold check
455 		if (timer_threshold[3] && timer[3] >= timer_threshold[3])
456 		{
457 			timer[3] = 0;
458 
459 			Z80_irq();
460 			TestIntHDMA(10, 0x13);
461 		}
462 	}
463 	return(ret);
464 }
465 
reset_timers(void)466 void reset_timers(void)
467 {
468 	timer_hint = 0;
469 
470 	memset(timer, 0, sizeof(timer));
471 	memset(timer_clock, 0, sizeof(timer_clock));
472 	memset(timer_threshold, 0, sizeof(timer_threshold));
473 
474 	timer0 = false;
475 	timer2 = false;
476 }
477 
reset_int(void)478 void reset_int(void)
479 {
480  TRUN = 0;
481  T01MOD = 0;
482  T23MOD = 0;
483  TRDC = 0;
484  TFFCR = 0;
485 
486  memset(HDMAStartVector, 0, sizeof(HDMAStartVector));
487  memset(ipending, 0, sizeof(ipending));
488  memset(IntPrio, 0, sizeof(IntPrio));
489 
490  h_int = false;
491 }
492 
timer_write8(uint32 address,uint8 data)493 void timer_write8(uint32 address, uint8 data)
494 {
495 	switch(address)
496 	{
497 	 case 0x20: TRUN = data;
498                     if ((TRUN & 0x01) == 0)         timer[0] = 0;
499     	            if ((TRUN & 0x02) == 0)         timer[1] = 0;
500         	    if ((TRUN & 0x04) == 0)         timer[2] = 0;
501                     if ((TRUN & 0x08) == 0)         timer[3] = 0;
502 		    break;
503 	 case 0x22: timer_threshold[0] = data; break;
504 	 case 0x23: timer_threshold[1] = data; break;
505 	 case 0x24: T01MOD = data; break;
506 	 case 0x25: TFFCR = data & 0x33; break;
507 	 case 0x26: timer_threshold[2] = data; break;
508 	 case 0x27: timer_threshold[3] = data; break;
509 	 case 0x28: T23MOD = data; break;
510 	 case 0x29: TRDC = data & 0x3; break;
511 	}
512 }
513 
timer_read8(uint32 address)514 uint8 timer_read8(uint32 address)
515 {
516 	uint8 ret = 0;
517 
518 	switch(address)
519 	{
520 	 //default: printf("Baaaad: %08x\n", address); break;
521 	 // Cool boarders is stupid and tries to read from a write-only register >_<
522 	 // Returning 4 makes the game run ok, so 4 it is!
523 	 default: ret = 0x4; break;
524 	 case 0x20: ret = TRUN; break;
525 	 case 0x29: ret = TRDC; break;
526 	}
527 
528 	//printf("UNK B R: %08x\n", address);
529 	return(ret);
530 }
531 
int_timer_StateAction(StateMem * sm,int load,int data_only)532 int int_timer_StateAction(StateMem *sm, int load, int data_only)
533 {
534  SFORMAT StateRegs[] =
535  {
536   SFVAR(timer_hint),
537   SFVAR(timer_clock),
538   SFVAR(timer),
539   SFVAR(timer_threshold),
540   SFVAR(TRUN),
541   SFVAR(T01MOD), SFVAR(T23MOD),
542   SFVAR(TRDC),
543   SFVAR(TFFCR),
544   SFVAR(HDMAStartVector),
545   SFVAR(ipending),
546   SFVAR(IntPrio),
547   SFVAR(h_int),
548   SFVAR(timer0),
549   SFVAR(timer2),
550   SFEND
551  };
552  if(!MDFNSS_StateAction(sm, load, data_only, StateRegs, "INTT"))
553   return(0);
554  return(1);
555 }
556 
557 }
558