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