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