|
|
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.1.11! root 70: #include <assert.h>
1.1 root 71: #include "main.h"
1.1.1.9 root 72: #include "blitter.h"
1.1.1.6 root 73: #include "dmaSnd.h"
1.1.1.8 root 74: #include "fdc.h"
1.1 root 75: #include "ikbd.h"
76: #include "int.h"
77: #include "m68000.h"
78: #include "mfp.h"
1.1.1.10 root 79: #include "midi.h"
80: #include "memorySnapShot.h"
1.1 root 81: #include "sound.h"
82: #include "video.h"
83:
1.1.1.3 root 84:
1.1.1.8 root 85: void (*PendingInterruptFunction)(void);
86: int PendingInterruptCount;
1.1.1.7 root 87:
1.1.1.8 root 88: static int nCyclesOver;
1.1.1.7 root 89:
1.1.1.8 root 90: /* List of possible interrupt handlers to be store in 'PendingInterruptTable',
91: * used for 'MemorySnapShot' */
92: static void (* const pIntHandlerFunctions[MAX_INTERRUPTS])(void) =
93: {
94: NULL,
95: Video_InterruptHandler_VBL,
96: Video_InterruptHandler_HBL,
97: Video_InterruptHandler_EndLine,
98: MFP_InterruptHandler_TimerA,
99: MFP_InterruptHandler_TimerB,
100: MFP_InterruptHandler_TimerC,
101: MFP_InterruptHandler_TimerD,
102: IKBD_InterruptHandler_ResetTimer,
103: IKBD_InterruptHandler_ACIA,
1.1.1.9 root 104: IKBD_InterruptHandler_MFP,
1.1.1.10 root 105: IKBD_InterruptHandler_AutoSend,
1.1.1.8 root 106: DmaSnd_InterruptHandler,
1.1.1.9 root 107: DmaSnd_InterruptHandler_Microwire,
1.1.1.11! root 108: DmaSnd_InterruptHandler_DspXmit,
1.1.1.9 root 109: FDC_InterruptHandler_Update,
1.1.1.10 root 110: Blitter_InterruptHandler,
111: Midi_InterruptHandler_Update
1.1 root 112: };
113:
1.1.1.8 root 114: /* Event timer structure - keeps next timer to occur in structure so don't need
115: * to check all entries */
1.1.1.7 root 116: typedef struct
117: {
1.1.1.9 root 118: bool bUsed; /* Is interrupt active? */
119: Sint64 Cycles;
1.1.1.8 root 120: void (*pFunction)(void);
1.1.1.7 root 121: } INTERRUPTHANDLER;
122:
123: static INTERRUPTHANDLER InterruptHandlers[MAX_INTERRUPTS];
124: static int ActiveInterrupt=0;
1.1 root 125:
1.1.1.9 root 126: static void Int_SetNewInterrupt(void);
1.1.1.2 root 127:
128: /*-----------------------------------------------------------------------*/
1.1.1.8 root 129: /**
130: * Reset interrupts, handlers
131: */
1.1 root 132: void Int_Reset(void)
133: {
1.1.1.8 root 134: int i;
1.1 root 135:
1.1.1.8 root 136: /* Reset counts */
137: PendingInterruptCount = 0;
138: ActiveInterrupt = 0;
139: nCyclesOver = 0;
1.1 root 140:
1.1.1.8 root 141: /* Reset interrupt table */
142: for (i=0; i<MAX_INTERRUPTS; i++)
143: {
1.1.1.11! root 144: InterruptHandlers[i].bUsed = false;
1.1.1.9 root 145: InterruptHandlers[i].Cycles = INT_MAX;
1.1.1.8 root 146: InterruptHandlers[i].pFunction = pIntHandlerFunctions[i];
147: }
1.1 root 148: }
149:
1.1.1.2 root 150:
151: /*-----------------------------------------------------------------------*/
1.1.1.8 root 152: /**
153: * Convert interrupt handler function pointer to ID, used for saving
154: */
155: static int Int_HandlerFunctionToID(void (*pHandlerFunction)(void))
1.1 root 156: {
1.1.1.8 root 157: int i;
1.1 root 158:
1.1.1.8 root 159: /* Scan for function match */
160: for (i=0; i<MAX_INTERRUPTS; i++)
161: {
162: if (pIntHandlerFunctions[i]==pHandlerFunction)
163: return i;
164: }
165:
166: /* Didn't find one! Oops */
167: fprintf(stderr, "\nError: didn't find interrupt function matching 0x%p\n",
168: pHandlerFunction);
169: return 0;
1.1 root 170: }
171:
1.1.1.2 root 172:
173: /*-----------------------------------------------------------------------*/
1.1.1.8 root 174: /**
175: * Convert ID back into interrupt handler function, used for restoring
176: */
177: static void *Int_IDToHandlerFunction(int ID)
1.1 root 178: {
1.1.1.8 root 179: /* Get function pointer */
180: return pIntHandlerFunctions[ID];
1.1 root 181: }
182:
1.1.1.2 root 183:
184: /*-----------------------------------------------------------------------*/
1.1.1.8 root 185: /**
186: * Save/Restore snapshot of local variables('MemorySnapShot_Store' handles type)
187: */
1.1.1.9 root 188: void Int_MemorySnapShot_Capture(bool bSave)
1.1 root 189: {
1.1.1.8 root 190: int i,ID;
191:
192: /* Save/Restore details */
193: for (i=0; i<MAX_INTERRUPTS; i++)
194: {
195: MemorySnapShot_Store(&InterruptHandlers[i].bUsed, sizeof(InterruptHandlers[i].bUsed));
196: MemorySnapShot_Store(&InterruptHandlers[i].Cycles, sizeof(InterruptHandlers[i].Cycles));
197: if (bSave)
198: {
199: /* Convert function to ID */
200: ID = Int_HandlerFunctionToID(InterruptHandlers[i].pFunction);
201: MemorySnapShot_Store(&ID, sizeof(int));
202: }
203: else
204: {
205: /* Convert ID to function */
206: MemorySnapShot_Store(&ID, sizeof(int));
207: InterruptHandlers[i].pFunction = Int_IDToHandlerFunction(ID);
208: }
209: }
210: MemorySnapShot_Store(&nCyclesOver, sizeof(nCyclesOver));
211: MemorySnapShot_Store(&PendingInterruptCount, sizeof(PendingInterruptCount));
212: if (bSave)
213: {
214: /* Convert function to ID */
215: ID = Int_HandlerFunctionToID(PendingInterruptFunction);
216: MemorySnapShot_Store(&ID, sizeof(int));
217: }
218: else
219: {
220: /* Convert ID to function */
221: MemorySnapShot_Store(&ID, sizeof(int));
222: PendingInterruptFunction = Int_IDToHandlerFunction(ID);
223: }
1.1.1.9 root 224:
225:
226: if (!bSave)
227: Int_SetNewInterrupt(); /* when restoring snapshot, compute current state after */
1.1 root 228: }
229:
1.1.1.2 root 230:
231: /*-----------------------------------------------------------------------*/
1.1.1.8 root 232: /**
1.1.1.9 root 233: * Find next interrupt to occur, and store to global variables for decrement
234: * in instruction decode loop.
235: * Note: Although InterruptHandlers.Cycles and LowestCycleCount are 64 bit
236: * variables to get all the cycle counters right (e.g. the DMA sound counter
237: * can get very high), PendingInterruptCount is still a 32 bit variable for
238: * performance reasons (it's decremented after each CPU instruction).
239: * So we have to initialize LowestCycleCount with INT_MAX, not with INT64_MAX!
240: * Since there is always a VBL or HBL counter pending which fits fine into the
241: * 32 bit variable, we can be sure that we don't run into problems here.
1.1.1.8 root 242: */
1.1.1.7 root 243: static void Int_SetNewInterrupt(void)
1.1 root 244: {
1.1.1.9 root 245: Sint64 LowestCycleCount = INT_MAX;
1.1.1.11! root 246: interrupt_id LowestInterrupt = INTERRUPT_NULL, i;
1.1 root 247:
1.1.1.11! root 248: LOG_TRACE(TRACE_INT, "int set new in video_cyc=%d active_int=%d pending_count=%d\n",
! 249: Cycles_GetCounter(CYCLES_COUNTER_VIDEO), ActiveInterrupt, PendingInterruptCount);
1.1.1.2 root 250:
1.1.1.8 root 251: /* Find next interrupt to go off */
1.1.1.11! root 252: for (i = INTERRUPT_NULL+1; i < MAX_INTERRUPTS; i++)
1.1.1.8 root 253: {
254: /* Is interrupt pending? */
255: if (InterruptHandlers[i].bUsed)
256: {
257: if (InterruptHandlers[i].Cycles < LowestCycleCount)
258: {
259: LowestCycleCount = InterruptHandlers[i].Cycles;
260: LowestInterrupt = i;
261: }
262: }
263: }
1.1 root 264:
1.1.1.8 root 265: /* Set new counts, active interrupt */
266: PendingInterruptCount = InterruptHandlers[LowestInterrupt].Cycles;
267: PendingInterruptFunction = InterruptHandlers[LowestInterrupt].pFunction;
268: ActiveInterrupt = LowestInterrupt;
269:
1.1.1.11! root 270: LOG_TRACE(TRACE_INT, "int set new out video_cyc=%d active_int=%d pending_count=%d\n",
1.1.1.8 root 271: Cycles_GetCounter(CYCLES_COUNTER_VIDEO), ActiveInterrupt, PendingInterruptCount );
1.1 root 272: }
273:
1.1.1.2 root 274:
275: /*-----------------------------------------------------------------------*/
1.1.1.8 root 276: /**
277: * Adjust all interrupt timings, MUST call Int_SetNewInterrupt after this.
278: */
1.1.1.3 root 279: static void Int_UpdateInterrupt(void)
1.1 root 280: {
1.1.1.9 root 281: Sint64 CycleSubtract;
1.1.1.8 root 282: int i;
1.1 root 283:
1.1.1.8 root 284: /* Find out how many cycles we went over (<=0) */
285: nCyclesOver = PendingInterruptCount;
286: /* Calculate how many cycles have passed, included time we went over */
1.1.1.9 root 287: CycleSubtract = InterruptHandlers[ActiveInterrupt].Cycles - nCyclesOver;
1.1 root 288:
1.1.1.8 root 289: /* Adjust table */
290: for (i = 0; i < MAX_INTERRUPTS; i++)
291: {
292: if (InterruptHandlers[i].bUsed)
293: InterruptHandlers[i].Cycles -= CycleSubtract;
294: }
295:
1.1.1.11! root 296: LOG_TRACE(TRACE_INT, "int upd video_cyc=%d cycle_over=%d cycle_sub=%lld\n",
1.1.1.8 root 297: Cycles_GetCounter(CYCLES_COUNTER_VIDEO), nCyclesOver, CycleSubtract );
1.1 root 298: }
299:
1.1.1.2 root 300:
301: /*-----------------------------------------------------------------------*/
1.1.1.8 root 302: /**
303: * Adjust all interrupt timings as 'ActiveInterrupt' has occured, and
304: * remove from active list.
305: */
1.1 root 306: void Int_AcknowledgeInterrupt(void)
307: {
1.1.1.8 root 308: /* Update list cycle counts */
309: Int_UpdateInterrupt();
1.1 root 310:
1.1.1.8 root 311: /* Disable interrupt entry which has just occured */
1.1.1.11! root 312: InterruptHandlers[ActiveInterrupt].bUsed = false;
1.1 root 313:
1.1.1.8 root 314: /* Set new */
315: Int_SetNewInterrupt();
316:
1.1.1.11! root 317: LOG_TRACE(TRACE_INT, "int ack video_cyc=%d active_int=%d active_cyc=%d pending_count=%d\n",
1.1.1.9 root 318: Cycles_GetCounter(CYCLES_COUNTER_VIDEO), ActiveInterrupt, (int)InterruptHandlers[ActiveInterrupt].Cycles, PendingInterruptCount );
1.1 root 319: }
320:
1.1.1.2 root 321:
322: /*-----------------------------------------------------------------------*/
1.1.1.8 root 323: /**
324: * Add interrupt from time last one occurred.
325: */
326: void Int_AddAbsoluteInterrupt(int CycleTime, int CycleType, interrupt_id Handler)
1.1 root 327: {
1.1.1.11! root 328: assert(CycleTime >= 0);
! 329:
1.1.1.8 root 330: /* Update list cycle counts before adding a new one, */
331: /* since Int_SetNewInterrupt can change the active int / PendingInterruptCount */
1.1.1.11! root 332: if ( ( ActiveInterrupt > 0 ) && ( PendingInterruptCount > 0 ) )
! 333: Int_UpdateInterrupt();
1.1.1.8 root 334:
1.1.1.11! root 335: InterruptHandlers[Handler].bUsed = true;
1.1.1.9 root 336: InterruptHandlers[Handler].Cycles = INT_CONVERT_TO_INTERNAL((Sint64)CycleTime , CycleType) + nCyclesOver;
1.1.1.8 root 337:
338: /* Set new */
339: Int_SetNewInterrupt();
1.1 root 340:
1.1.1.11! root 341: LOG_TRACE(TRACE_INT, "int add abs video_cyc=%d handler=%d handler_cyc=%lld pending_count=%d\n",
1.1.1.8 root 342: Cycles_GetCounter(CYCLES_COUNTER_VIDEO), Handler, InterruptHandlers[Handler].Cycles, PendingInterruptCount );
1.1 root 343: }
344:
1.1.1.2 root 345:
346: /*-----------------------------------------------------------------------*/
1.1.1.8 root 347: /**
1.1.1.9 root 348: * Add interrupt to occur from now.
1.1.1.8 root 349: */
1.1.1.9 root 350: void Int_AddRelativeInterrupt(int CycleTime, int CycleType, interrupt_id Handler)
1.1 root 351: {
1.1.1.9 root 352: Int_AddRelativeInterruptWithOffset(CycleTime, CycleType, Handler, 0);
1.1 root 353: }
354:
1.1.1.2 root 355:
356: /*-----------------------------------------------------------------------*/
1.1.1.8 root 357: /**
358: * Add interrupt to occur from now without offset
359: */
1.1.1.11! root 360: #if 0
1.1.1.8 root 361: void Int_AddRelativeInterruptNoOffset(int CycleTime, int CycleType, interrupt_id Handler)
1.1 root 362: {
1.1.1.8 root 363: /* Update list cycle counts before adding a new one, */
364: /* since Int_SetNewInterrupt can change the active int / PendingInterruptCount */
365: if ( ( ActiveInterrupt > 0 ) && ( PendingInterruptCount > 0 ) )
366: Int_UpdateInterrupt();
367:
368: // nCyclesOver = 0;
1.1.1.11! root 369: InterruptHandlers[Handler].bUsed = true;
1.1.1.9 root 370: InterruptHandlers[Handler].Cycles = INT_CONVERT_TO_INTERNAL((Sint64)CycleTime , CycleType) + PendingInterruptCount;
1.1 root 371:
1.1.1.8 root 372: /* Set new */
373: Int_SetNewInterrupt();
374:
1.1.1.11! root 375: LOG_TRACE(TRACE_INT, "int add rel no_off video_cyc=%d handler=%d handler_cyc=%lld pending_count=%d\n",
1.1.1.8 root 376: Cycles_GetCounter(CYCLES_COUNTER_VIDEO), Handler, InterruptHandlers[Handler].Cycles, PendingInterruptCount );
1.1 root 377: }
1.1.1.11! root 378: #endif
1.1 root 379:
1.1.1.2 root 380:
381: /*-----------------------------------------------------------------------*/
1.1.1.8 root 382: /**
1.1.1.9 root 383: * Add interrupt to occur after CycleTime/CycleType + CycleOffset.
384: * CycleOffset can be used to add another delay to the resulting
385: * number of internal cycles (should be 0 most of the time, except in
386: * the MFP emulation to start timers precisely based on the number of
387: * cycles of the current instruction).
388: * This allows to restart an MFP timer just after it expired.
1.1.1.8 root 389: */
390: void Int_AddRelativeInterruptWithOffset(int CycleTime, int CycleType, interrupt_id Handler, int CycleOffset)
1.1 root 391: {
1.1.1.11! root 392: assert(CycleTime >= 0);
! 393:
1.1.1.8 root 394: /* Update list cycle counts before adding a new one, */
395: /* since Int_SetNewInterrupt can change the active int / PendingInterruptCount */
396: if ( ( ActiveInterrupt > 0 ) && ( PendingInterruptCount > 0 ) )
397: Int_UpdateInterrupt();
1.1 root 398:
1.1.1.11! root 399: InterruptHandlers[Handler].bUsed = true;
1.1.1.9 root 400: InterruptHandlers[Handler].Cycles = INT_CONVERT_TO_INTERNAL((Sint64)CycleTime , CycleType) + CycleOffset;
1.1.1.8 root 401:
402: /* Set new */
403: Int_SetNewInterrupt();
404:
1.1.1.11! root 405: LOG_TRACE(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 406: Cycles_GetCounter(CYCLES_COUNTER_VIDEO), Handler, InterruptHandlers[Handler].Cycles, CycleOffset, PendingInterruptCount );
1.1 root 407: }
408:
1.1.1.2 root 409:
410: /*-----------------------------------------------------------------------*/
1.1.1.8 root 411: /**
412: * Remove a pending interrupt from our table
413: */
414: void Int_RemovePendingInterrupt(interrupt_id Handler)
415: {
416: /* Update list cycle counts, including the handler we want to remove */
417: /* to be able to resume it later (for MFP timers) */
418: Int_UpdateInterrupt();
419:
420: /* Stop interrupt after Int_UpdateInterrupt, for Int_ResumeStoppedInterrupt */
1.1.1.11! root 421: InterruptHandlers[Handler].bUsed = false;
1.1.1.8 root 422:
423: /* Set new */
424: Int_SetNewInterrupt();
425:
1.1.1.11! root 426: LOG_TRACE(TRACE_INT, "int remove pending video_cyc=%d handler=%d handler_cyc=%lld pending_count=%d\n",
1.1.1.8 root 427: Cycles_GetCounter(CYCLES_COUNTER_VIDEO), Handler, InterruptHandlers[Handler].Cycles, PendingInterruptCount );
428: }
429:
430:
431: /*-----------------------------------------------------------------------*/
432: /**
433: * Resume a stopped interrupt from its current cycle count (for MFP timers)
434: */
435: void Int_ResumeStoppedInterrupt(interrupt_id Handler)
1.1 root 436: {
1.1.1.8 root 437: /* Restart interrupt */
1.1.1.11! root 438: InterruptHandlers[Handler].bUsed = true;
1.1.1.8 root 439:
440: /* Update list cycle counts */
441: Int_UpdateInterrupt();
442: /* Set new */
443: Int_SetNewInterrupt();
444:
1.1.1.11! root 445: LOG_TRACE(TRACE_INT, "int resume stopped video_cyc=%d handler=%d handler_cyc=%lld pending_count=%d\n",
1.1.1.8 root 446: Cycles_GetCounter(CYCLES_COUNTER_VIDEO), Handler, InterruptHandlers[Handler].Cycles, PendingInterruptCount );
447: }
448:
449:
450: /*-----------------------------------------------------------------------*/
451: /**
1.1.1.11! root 452: * Return true if interrupt is active in list
1.1.1.8 root 453: */
1.1.1.9 root 454: bool Int_InterruptActive(interrupt_id Handler)
1.1.1.8 root 455: {
456: /* Is timer active? */
457: if (InterruptHandlers[Handler].bUsed)
1.1.1.11! root 458: return true;
1.1.1.8 root 459:
1.1.1.11! root 460: return false;
1.1.1.8 root 461: }
462:
463:
464: /*-----------------------------------------------------------------------*/
465: /**
466: * Return cycles passed for an interrupt handler
467: */
468: int Int_FindCyclesPassed(interrupt_id Handler, int CycleType)
469: {
1.1.1.9 root 470: Sint64 CyclesPassed, CyclesFromLastInterrupt;
1.1.1.8 root 471:
472: CyclesFromLastInterrupt = InterruptHandlers[ActiveInterrupt].Cycles - PendingInterruptCount;
473: CyclesPassed = InterruptHandlers[Handler].Cycles - CyclesFromLastInterrupt;
1.1 root 474:
1.1.1.11! root 475: LOG_TRACE(TRACE_INT, "int find passed cyc video_cyc=%d handler=%d last_cyc=%lld passed_cyc=%lld\n",
1.1.1.8 root 476: Cycles_GetCounter(CYCLES_COUNTER_VIDEO), Handler, CyclesFromLastInterrupt, CyclesPassed );
1.1 root 477:
1.1.1.8 root 478: return INT_CONVERT_FROM_INTERNAL ( CyclesPassed , CycleType ) ;
1.1 root 479: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.