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