|
|
1.1 root 1: /*
1.1.1.3 root 2: Hatari - int.c
3:
4: This file is distributed under the GNU Public License, version 2 or at
5: your option any later version. Read the file gpl.txt for details.
1.1 root 6:
1.1.1.8 root 7: This code handles our interrupt table. So we do not need to test for every
8: possible interrupt we add any pending interrupts into a table. We then scan
9: the list if used entries in the table and copy the one with the least cycle
10: count into the global 'PendingInterruptCount' variable. This is then
11: decremented by the execution loop - rather than decrement each and every
12: entry (as the others cannot occur before this one).
13: We have two methods of adding interrupts; Absolute and Relative.
14: Absolute will set values from the time of the previous interrupt (e.g., add
15: HBL every 512 cycles), and Relative will add from the current cycle time.
16: Note that interrupt may occur 'late'. I.e., if an interrupt is due in 4
17: cycles time but the current instruction takes 20 cycles we will be 16 cycles
18: late - this is handled in the adjust functions.
19:
20:
21: In order to handle both CPU and MFP interrupts, we don't convert MFP cyles to CPU cycles,
22: because it requires some floating points approximation and accumulates some errors that
23: could lead to bad results.
24: Instead, CPU and MFP cycles are converted to 'internal' cycles with the following rule :
25: - 1 CPU cycle gives 9600 internal cycles
26: - 1 MFP cycle gives 31333 internal cycle
27:
28: All interrupts are then handled in the 'internal' units and are converted back to cpu or mfp
29: units when needed. This allows very good synchronisation between CPU and MFP, without the
30: rounding errors of floating points math.
31:
32: Thanks to Arnaud Carre (Leonard / Oxygene) for sharing this method used in Saint (and also
33: used in sc68).
34:
35: Conversions are based on these values :
36: real MFP frequency is 2457600 Hz
37: real CPU frequency is 8021247 Hz (PAL european STF), which we round to 8021248.
38:
39: Then :
40: 8021248 = ( 2^8 * 31333 )
41: 2457600 = ( 2^15 * 3 * 5^2 )
42:
43: So, the ratio 8021248 / 2457600 can be expressed as 31333 / 9600
44:
1.1 root 45: */
1.1.1.8 root 46:
47:
48: /* 2007/05/08 [NP] Call Int_UpdateInterrupt in Int_AddRelativeInterrupt, because */
49: /* Int_SetNewInterrupt can change the active int / PendingInterruptCount */
50: /* 2007/09/29 [NP] New method to handle interrupt. Instead of using cpu cycles to store */
51: /* video int and MFP int (which looses precision because MFP cycles need */
52: /* to be rounded to cpu cycles), we're using an 'internal' unit to store */
53: /* both cpu cycles and mfp cycles. This removes floating point operations */
54: /* and greatly improve the precision for MFP int with a very small divisor */
55: /* (thanks to Leonard / Oxygene for the idea, also used in 'sc68'). */
56: /* 2007/10/21 [NP] Function 'Int_AddRelativeInterruptWithOffset' to restart an MFP timer */
57: /* just after it expired (ULM DSOTS and Overscan Demos). */
58: /* 2007/10/28 [NP] Function 'Int_ResumeStoppedInterrupt' to continue an interrupt from */
59: /* where it was stopped, without updating 'cycles'. */
60: /* 2007/12/27 [NP] Parameter 'AddInternalCycle' in Int_AddRelativeInterrupt, used in mfp.c */
61: /* to precisely start a timer after the current inst. */
1.1.1.9 root 62: /* 2008/03/30 [NP] ActiveInterrupt was not saved in the memory snapshot, which could cause */
63: /* errors when restoring it. We call Int_SetNewInterrupt() after restoring */
64: /* the snapshot to set ActiveInterrupt to the correct value. */
1.1.1.8 root 65:
66:
1.1.1.10! root 67: const char Int_fileid[] = "Hatari int.c : " __DATE__ " " __TIME__;
1.1 root 68:
1.1.1.9 root 69: #include <stdint.h>
1.1 root 70: #include "main.h"
1.1.1.9 root 71: #include "blitter.h"
1.1.1.6 root 72: #include "dmaSnd.h"
1.1.1.8 root 73: #include "fdc.h"
1.1 root 74: #include "ikbd.h"
75: #include "int.h"
76: #include "m68000.h"
77: #include "mfp.h"
1.1.1.10! root 78: #include "midi.h"
! 79: #include "memorySnapShot.h"
1.1 root 80: #include "sound.h"
81: #include "video.h"
82:
1.1.1.3 root 83:
1.1.1.8 root 84: void (*PendingInterruptFunction)(void);
85: int PendingInterruptCount;
1.1.1.7 root 86:
1.1.1.8 root 87: static int nCyclesOver;
1.1.1.7 root 88:
1.1.1.8 root 89: /* List of possible interrupt handlers to be store in 'PendingInterruptTable',
90: * used for 'MemorySnapShot' */
91: static void (* const pIntHandlerFunctions[MAX_INTERRUPTS])(void) =
92: {
93: NULL,
94: Video_InterruptHandler_VBL,
95: Video_InterruptHandler_HBL,
96: Video_InterruptHandler_EndLine,
97: MFP_InterruptHandler_TimerA,
98: MFP_InterruptHandler_TimerB,
99: MFP_InterruptHandler_TimerC,
100: MFP_InterruptHandler_TimerD,
101: IKBD_InterruptHandler_ResetTimer,
102: IKBD_InterruptHandler_ACIA,
1.1.1.9 root 103: IKBD_InterruptHandler_MFP,
1.1.1.10! root 104: IKBD_InterruptHandler_AutoSend,
1.1.1.8 root 105: DmaSnd_InterruptHandler,
1.1.1.9 root 106: DmaSnd_InterruptHandler_Microwire,
107: FDC_InterruptHandler_Update,
1.1.1.10! root 108: Blitter_InterruptHandler,
! 109: Midi_InterruptHandler_Update
1.1 root 110: };
111:
1.1.1.8 root 112: /* Event timer structure - keeps next timer to occur in structure so don't need
113: * to check all entries */
1.1.1.7 root 114: typedef struct
115: {
1.1.1.9 root 116: bool bUsed; /* Is interrupt active? */
117: Sint64 Cycles;
1.1.1.8 root 118: void (*pFunction)(void);
1.1.1.7 root 119: } INTERRUPTHANDLER;
120:
121: static INTERRUPTHANDLER InterruptHandlers[MAX_INTERRUPTS];
122: static int ActiveInterrupt=0;
1.1 root 123:
1.1.1.9 root 124: static void Int_SetNewInterrupt(void);
1.1.1.2 root 125:
126: /*-----------------------------------------------------------------------*/
1.1.1.8 root 127: /**
128: * Reset interrupts, handlers
129: */
1.1 root 130: void Int_Reset(void)
131: {
1.1.1.8 root 132: int i;
1.1 root 133:
1.1.1.8 root 134: /* Reset counts */
135: PendingInterruptCount = 0;
136: ActiveInterrupt = 0;
137: nCyclesOver = 0;
1.1 root 138:
1.1.1.8 root 139: /* Reset interrupt table */
140: for (i=0; i<MAX_INTERRUPTS; i++)
141: {
142: InterruptHandlers[i].bUsed = FALSE;
1.1.1.9 root 143: InterruptHandlers[i].Cycles = INT_MAX;
1.1.1.8 root 144: InterruptHandlers[i].pFunction = pIntHandlerFunctions[i];
145: }
1.1 root 146: }
147:
1.1.1.2 root 148:
149: /*-----------------------------------------------------------------------*/
1.1.1.8 root 150: /**
151: * Convert interrupt handler function pointer to ID, used for saving
152: */
153: static int Int_HandlerFunctionToID(void (*pHandlerFunction)(void))
1.1 root 154: {
1.1.1.8 root 155: int i;
1.1 root 156:
1.1.1.8 root 157: /* Scan for function match */
158: for (i=0; i<MAX_INTERRUPTS; i++)
159: {
160: if (pIntHandlerFunctions[i]==pHandlerFunction)
161: return i;
162: }
163:
164: /* Didn't find one! Oops */
165: fprintf(stderr, "\nError: didn't find interrupt function matching 0x%p\n",
166: pHandlerFunction);
167: return 0;
1.1 root 168: }
169:
1.1.1.2 root 170:
171: /*-----------------------------------------------------------------------*/
1.1.1.8 root 172: /**
173: * Convert ID back into interrupt handler function, used for restoring
174: */
175: static void *Int_IDToHandlerFunction(int ID)
1.1 root 176: {
1.1.1.8 root 177: /* Get function pointer */
178: return pIntHandlerFunctions[ID];
1.1 root 179: }
180:
1.1.1.2 root 181:
182: /*-----------------------------------------------------------------------*/
1.1.1.8 root 183: /**
184: * Save/Restore snapshot of local variables('MemorySnapShot_Store' handles type)
185: */
1.1.1.9 root 186: void Int_MemorySnapShot_Capture(bool bSave)
1.1 root 187: {
1.1.1.8 root 188: int i,ID;
189:
190: /* Save/Restore details */
191: for (i=0; i<MAX_INTERRUPTS; i++)
192: {
193: MemorySnapShot_Store(&InterruptHandlers[i].bUsed, sizeof(InterruptHandlers[i].bUsed));
194: MemorySnapShot_Store(&InterruptHandlers[i].Cycles, sizeof(InterruptHandlers[i].Cycles));
195: if (bSave)
196: {
197: /* Convert function to ID */
198: ID = Int_HandlerFunctionToID(InterruptHandlers[i].pFunction);
199: MemorySnapShot_Store(&ID, sizeof(int));
200: }
201: else
202: {
203: /* Convert ID to function */
204: MemorySnapShot_Store(&ID, sizeof(int));
205: InterruptHandlers[i].pFunction = Int_IDToHandlerFunction(ID);
206: }
207: }
208: MemorySnapShot_Store(&nCyclesOver, sizeof(nCyclesOver));
209: MemorySnapShot_Store(&PendingInterruptCount, sizeof(PendingInterruptCount));
210: if (bSave)
211: {
212: /* Convert function to ID */
213: ID = Int_HandlerFunctionToID(PendingInterruptFunction);
214: MemorySnapShot_Store(&ID, sizeof(int));
215: }
216: else
217: {
218: /* Convert ID to function */
219: MemorySnapShot_Store(&ID, sizeof(int));
220: PendingInterruptFunction = Int_IDToHandlerFunction(ID);
221: }
1.1.1.9 root 222:
223:
224: if (!bSave)
225: Int_SetNewInterrupt(); /* when restoring snapshot, compute current state after */
1.1 root 226: }
227:
1.1.1.2 root 228:
229: /*-----------------------------------------------------------------------*/
1.1.1.8 root 230: /**
1.1.1.9 root 231: * Find next interrupt to occur, and store to global variables for decrement
232: * in instruction decode loop.
233: * Note: Although InterruptHandlers.Cycles and LowestCycleCount are 64 bit
234: * variables to get all the cycle counters right (e.g. the DMA sound counter
235: * can get very high), PendingInterruptCount is still a 32 bit variable for
236: * performance reasons (it's decremented after each CPU instruction).
237: * So we have to initialize LowestCycleCount with INT_MAX, not with INT64_MAX!
238: * Since there is always a VBL or HBL counter pending which fits fine into the
239: * 32 bit variable, we can be sure that we don't run into problems here.
1.1.1.8 root 240: */
1.1.1.7 root 241: static void Int_SetNewInterrupt(void)
1.1 root 242: {
1.1.1.9 root 243: Sint64 LowestCycleCount = INT_MAX;
244: interrupt_id LowestInterrupt = 0, i;
1.1 root 245:
1.1.1.8 root 246: HATARI_TRACE ( HATARI_TRACE_INT , "int set new in video_cyc=%d active_int=%d pending_count=%d\n",
247: Cycles_GetCounter(CYCLES_COUNTER_VIDEO), ActiveInterrupt, PendingInterruptCount );
1.1.1.2 root 248:
1.1.1.8 root 249: /* Find next interrupt to go off */
250: for (i = 1; i < MAX_INTERRUPTS; i++)
251: {
252: /* Is interrupt pending? */
253: if (InterruptHandlers[i].bUsed)
254: {
255: if (InterruptHandlers[i].Cycles < LowestCycleCount)
256: {
257: LowestCycleCount = InterruptHandlers[i].Cycles;
258: LowestInterrupt = i;
259: }
260: }
261: }
1.1 root 262:
1.1.1.8 root 263: /* Set new counts, active interrupt */
264: PendingInterruptCount = InterruptHandlers[LowestInterrupt].Cycles;
265: PendingInterruptFunction = InterruptHandlers[LowestInterrupt].pFunction;
266: ActiveInterrupt = LowestInterrupt;
267:
268: HATARI_TRACE ( HATARI_TRACE_INT , "int set new out video_cyc=%d active_int=%d pending_count=%d\n",
269: Cycles_GetCounter(CYCLES_COUNTER_VIDEO), ActiveInterrupt, PendingInterruptCount );
1.1 root 270: }
271:
1.1.1.2 root 272:
273: /*-----------------------------------------------------------------------*/
1.1.1.8 root 274: /**
275: * Adjust all interrupt timings, MUST call Int_SetNewInterrupt after this.
276: */
1.1.1.3 root 277: static void Int_UpdateInterrupt(void)
1.1 root 278: {
1.1.1.9 root 279: Sint64 CycleSubtract;
1.1.1.8 root 280: int i;
1.1 root 281:
1.1.1.8 root 282: /* Find out how many cycles we went over (<=0) */
283: nCyclesOver = PendingInterruptCount;
284: /* Calculate how many cycles have passed, included time we went over */
1.1.1.9 root 285: CycleSubtract = InterruptHandlers[ActiveInterrupt].Cycles - nCyclesOver;
1.1 root 286:
1.1.1.8 root 287: /* Adjust table */
288: for (i = 0; i < MAX_INTERRUPTS; i++)
289: {
290: if (InterruptHandlers[i].bUsed)
291: InterruptHandlers[i].Cycles -= CycleSubtract;
292: }
293:
1.1.1.9 root 294: HATARI_TRACE ( HATARI_TRACE_INT , "int upd video_cyc=%d cycle_over=%d cycle_sub=%lld\n",
1.1.1.8 root 295: Cycles_GetCounter(CYCLES_COUNTER_VIDEO), nCyclesOver, CycleSubtract );
1.1 root 296: }
297:
1.1.1.2 root 298:
299: /*-----------------------------------------------------------------------*/
1.1.1.8 root 300: /**
301: * Adjust all interrupt timings as 'ActiveInterrupt' has occured, and
302: * remove from active list.
303: */
1.1 root 304: void Int_AcknowledgeInterrupt(void)
305: {
1.1.1.8 root 306: /* Update list cycle counts */
307: Int_UpdateInterrupt();
1.1 root 308:
1.1.1.8 root 309: /* Disable interrupt entry which has just occured */
310: InterruptHandlers[ActiveInterrupt].bUsed = FALSE;
1.1 root 311:
1.1.1.8 root 312: /* Set new */
313: Int_SetNewInterrupt();
314:
315: HATARI_TRACE ( HATARI_TRACE_INT , "int ack video_cyc=%d active_int=%d active_cyc=%d pending_count=%d\n",
1.1.1.9 root 316: Cycles_GetCounter(CYCLES_COUNTER_VIDEO), ActiveInterrupt, (int)InterruptHandlers[ActiveInterrupt].Cycles, PendingInterruptCount );
1.1 root 317: }
318:
1.1.1.2 root 319:
320: /*-----------------------------------------------------------------------*/
1.1.1.8 root 321: /**
322: * Add interrupt from time last one occurred.
323: */
324: void Int_AddAbsoluteInterrupt(int CycleTime, int CycleType, interrupt_id Handler)
1.1 root 325: {
1.1.1.8 root 326: /* Update list cycle counts before adding a new one, */
327: /* since Int_SetNewInterrupt can change the active int / PendingInterruptCount */
328: /* [NP] FIXME : not necessary ? */
329: // if ( ( ActiveInterrupt > 0 ) && ( PendingInterruptCount > 0 ) )
330: // Int_UpdateInterrupt();
331:
332: InterruptHandlers[Handler].bUsed = TRUE;
1.1.1.9 root 333: InterruptHandlers[Handler].Cycles = INT_CONVERT_TO_INTERNAL((Sint64)CycleTime , CycleType) + nCyclesOver;
1.1.1.8 root 334:
335: /* Set new */
336: Int_SetNewInterrupt();
1.1 root 337:
1.1.1.9 root 338: HATARI_TRACE ( HATARI_TRACE_INT , "int add abs video_cyc=%d handler=%d handler_cyc=%lld pending_count=%d\n",
1.1.1.8 root 339: Cycles_GetCounter(CYCLES_COUNTER_VIDEO), Handler, InterruptHandlers[Handler].Cycles, PendingInterruptCount );
1.1 root 340: }
341:
1.1.1.2 root 342:
343: /*-----------------------------------------------------------------------*/
1.1.1.8 root 344: /**
1.1.1.9 root 345: * Add interrupt to occur from now.
1.1.1.8 root 346: */
1.1.1.9 root 347: void Int_AddRelativeInterrupt(int CycleTime, int CycleType, interrupt_id Handler)
1.1 root 348: {
1.1.1.9 root 349: Int_AddRelativeInterruptWithOffset(CycleTime, CycleType, Handler, 0);
1.1 root 350: }
351:
1.1.1.2 root 352:
353: /*-----------------------------------------------------------------------*/
1.1.1.8 root 354: /**
355: * Add interrupt to occur from now without offset
356: */
357: void Int_AddRelativeInterruptNoOffset(int CycleTime, int CycleType, interrupt_id Handler)
1.1 root 358: {
1.1.1.8 root 359: /* Update list cycle counts before adding a new one, */
360: /* since Int_SetNewInterrupt can change the active int / PendingInterruptCount */
361: if ( ( ActiveInterrupt > 0 ) && ( PendingInterruptCount > 0 ) )
362: Int_UpdateInterrupt();
363:
364: // nCyclesOver = 0;
365: InterruptHandlers[Handler].bUsed = TRUE;
1.1.1.9 root 366: InterruptHandlers[Handler].Cycles = INT_CONVERT_TO_INTERNAL((Sint64)CycleTime , CycleType) + PendingInterruptCount;
1.1 root 367:
1.1.1.8 root 368: /* Set new */
369: Int_SetNewInterrupt();
370:
1.1.1.9 root 371: HATARI_TRACE ( HATARI_TRACE_INT , "int add rel no_off video_cyc=%d handler=%d handler_cyc=%lld pending_count=%d\n",
1.1.1.8 root 372: Cycles_GetCounter(CYCLES_COUNTER_VIDEO), Handler, InterruptHandlers[Handler].Cycles, PendingInterruptCount );
1.1 root 373: }
374:
1.1.1.2 root 375:
376: /*-----------------------------------------------------------------------*/
1.1.1.8 root 377: /**
1.1.1.9 root 378: * Add interrupt to occur after CycleTime/CycleType + CycleOffset.
379: * CycleOffset can be used to add another delay to the resulting
380: * number of internal cycles (should be 0 most of the time, except in
381: * the MFP emulation to start timers precisely based on the number of
382: * cycles of the current instruction).
383: * This allows to restart an MFP timer just after it expired.
1.1.1.8 root 384: */
385: void Int_AddRelativeInterruptWithOffset(int CycleTime, int CycleType, interrupt_id Handler, int CycleOffset)
1.1 root 386: {
1.1.1.8 root 387: /* Update list cycle counts before adding a new one, */
388: /* since Int_SetNewInterrupt can change the active int / PendingInterruptCount */
389: if ( ( ActiveInterrupt > 0 ) && ( PendingInterruptCount > 0 ) )
390: Int_UpdateInterrupt();
1.1 root 391:
1.1.1.8 root 392: InterruptHandlers[Handler].bUsed = TRUE;
1.1.1.9 root 393: InterruptHandlers[Handler].Cycles = INT_CONVERT_TO_INTERNAL((Sint64)CycleTime , CycleType) + CycleOffset;
1.1.1.8 root 394:
395: /* Set new */
396: Int_SetNewInterrupt();
397:
1.1.1.9 root 398: HATARI_TRACE ( HATARI_TRACE_INT , "int add rel offset video_cyc=%d handler=%d handler_cyc=%lld offset_cyc=%d pending_count=%d\n",
1.1.1.8 root 399: Cycles_GetCounter(CYCLES_COUNTER_VIDEO), Handler, InterruptHandlers[Handler].Cycles, CycleOffset, PendingInterruptCount );
1.1 root 400: }
401:
1.1.1.2 root 402:
403: /*-----------------------------------------------------------------------*/
1.1.1.8 root 404: /**
405: * Remove a pending interrupt from our table
406: */
407: void Int_RemovePendingInterrupt(interrupt_id Handler)
408: {
409: /* Update list cycle counts, including the handler we want to remove */
410: /* to be able to resume it later (for MFP timers) */
411: Int_UpdateInterrupt();
412:
413: /* Stop interrupt after Int_UpdateInterrupt, for Int_ResumeStoppedInterrupt */
414: InterruptHandlers[Handler].bUsed = FALSE;
415:
416: /* Set new */
417: Int_SetNewInterrupt();
418:
1.1.1.9 root 419: HATARI_TRACE ( HATARI_TRACE_INT , "int remove pending video_cyc=%d handler=%d handler_cyc=%lld pending_count=%d\n",
1.1.1.8 root 420: Cycles_GetCounter(CYCLES_COUNTER_VIDEO), Handler, InterruptHandlers[Handler].Cycles, PendingInterruptCount );
421: }
422:
423:
424: /*-----------------------------------------------------------------------*/
425: /**
426: * Resume a stopped interrupt from its current cycle count (for MFP timers)
427: */
428: void Int_ResumeStoppedInterrupt(interrupt_id Handler)
1.1 root 429: {
1.1.1.8 root 430: /* Restart interrupt */
431: InterruptHandlers[Handler].bUsed = TRUE;
432:
433: /* Update list cycle counts */
434: Int_UpdateInterrupt();
435: /* Set new */
436: Int_SetNewInterrupt();
437:
1.1.1.9 root 438: HATARI_TRACE ( HATARI_TRACE_INT , "int resume stopped video_cyc=%d handler=%d handler_cyc=%lld pending_count=%d\n",
1.1.1.8 root 439: Cycles_GetCounter(CYCLES_COUNTER_VIDEO), Handler, InterruptHandlers[Handler].Cycles, PendingInterruptCount );
440: }
441:
442:
443: /*-----------------------------------------------------------------------*/
444: /**
445: * Return TRUE if interrupt is active in list
446: */
1.1.1.9 root 447: bool Int_InterruptActive(interrupt_id Handler)
1.1.1.8 root 448: {
449: /* Is timer active? */
450: if (InterruptHandlers[Handler].bUsed)
451: return TRUE;
452:
453: return FALSE;
454: }
455:
456:
457: /*-----------------------------------------------------------------------*/
458: /**
459: * Return cycles passed for an interrupt handler
460: */
461: int Int_FindCyclesPassed(interrupt_id Handler, int CycleType)
462: {
1.1.1.9 root 463: Sint64 CyclesPassed, CyclesFromLastInterrupt;
1.1.1.8 root 464:
465: CyclesFromLastInterrupt = InterruptHandlers[ActiveInterrupt].Cycles - PendingInterruptCount;
466: CyclesPassed = InterruptHandlers[Handler].Cycles - CyclesFromLastInterrupt;
1.1 root 467:
1.1.1.9 root 468: HATARI_TRACE ( HATARI_TRACE_INT , "int find passed cyc video_cyc=%d handler=%d last_cyc=%lld passed_cyc=%lld\n",
1.1.1.8 root 469: Cycles_GetCounter(CYCLES_COUNTER_VIDEO), Handler, CyclesFromLastInterrupt, CyclesPassed );
1.1 root 470:
1.1.1.8 root 471: return INT_CONVERT_FROM_INTERNAL ( CyclesPassed , CycleType ) ;
1.1 root 472: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.